ah mein schlechtes tldp.org/LDP/Bash-Beginners-Guide/html/sect_10_01.html Variablen schreibgeschützt machen. Diesen Variablen können dann weder durch nachfolgende Zuweisungsanweisungen Werte zugewiesen noch sie können nicht gesetzt werden.
Kokizzu
Normalerweise sind die Variablen schreibgeschützt, da / etc / profile viele Zeilen wie diese enthält readonly TMOUT. Ich ziehe es vor, diese Zeilen zu kommentieren und eine neue Verbindung zu diesem Linux-Computer herzustellen.
ROMANIA_engineer
1
@ ROMANIA_engineer Oder führen Sie einfach bash --norc aus und legen Sie dann das gewünschte Material manuell oder in Ihrer eigenen RC-Datei fest - zB: source ~ / .gnbashrc
Graham Nicholls
Antworten:
108
Tatsächlich können Sie eine schreibgeschützte Variable deaktivieren . aber ich muss warnen, dass dies eine hackige Methode ist. Hinzufügen dieser Antwort nur als Information, nicht als Empfehlung. Verwenden Sie es auf eigenes Risiko. Getestet auf Ubuntu 13.04, Bash 4.2.45.
Diese Methode beinhaltet das Kennen von Bash-Quellcode und er wird von dieser Antwort geerbt .
sudoMöglicherweise wird es basierend auf den ptrace_scope-Einstellungen Ihres Kernels benötigt oder nicht. Weitere Informationen finden Sie in den Kommentaren zur Antwort von vip9937.
Nun, das würde ich Redneck-Bash-Programmierung nennen;)
Floyd
5
Hinweis: nicht ändern verlockt cat << EOF| sudo gdbzu sudo gdb << EOF. Es kann nicht funktionieren, da die umgeleiteten Eingang Anbieter - bashwird aufgrund gestoppt gdbBefestigung.
Anishsane
Ich denke, diese Antwort ist etwas genauer, da sie kein
Sudo
1
^^ EOF bei stdin & explizites Beenden würde beide gdb sauber beenden.
Anishsane
2
Ich mag einen Liner:echo -e "attach $$\n call unbind_variable(\"PI\")\n detach" | gdb
Satya Mishra
48
Ich habe den obigen GDB-Hack ausprobiert, weil ich TMOUT deaktivieren möchte (um die automatische Abmeldung zu deaktivieren), aber auf dem Computer, auf dem TMOUT als schreibgeschützt festgelegt ist, darf ich sudo nicht verwenden. Aber da ich den Bash-Prozess besitze, brauche ich kein Sudo. Die Syntax funktionierte jedoch nicht ganz mit der Maschine, auf der ich mich befinde.
Dies hat jedoch funktioniert (ich habe es in meine .bashrc-Datei eingefügt):
# Disable the stupid auto-logoutunset TMOUT > /dev/null 2>&1
if [ $? -ne 0 ]; then
gdb <<EOF > /dev/null 2>&1
attach $$
call unbind_variable("TMOUT")
detach
quit
EOFfi
Obwohl -qund -nsind hilfreich, sie (nämlich -q) darf nicht stummgeschaltet gdb, so dass die /dev/nullUmleitung nach wie vor erforderlich ist. Toller Vorschlag, @LucasCimon
fbicknel
Irgendwelche Ideen, wie man etwas Ähnliches auf einer Maschine ohne GDB macht?
Lichtschalter05
1
@ Lightswitch05: siehe meine ctypes.sh Antwort
Wil
6
Laut Manpage:
unset [-fv] [name ...]
... Read-only variables may not be
unset. ...
Wenn Sie die Variable noch nicht exportiert haben, können Sie sie exec "$0" "$@"zum Neustart Ihrer Shell verwenden. Natürlich verlieren Sie auch alle anderen nicht exportierten Variablen. Wenn Sie eine neue Shell ohne starten exec, verliert sie anscheinend ihre schreibgeschützte Eigenschaft für diese Shell.
Die Verwendung von GDB ist furchtbar langsam. Versuchen Sie stattdessen ctypes.sh. Es funktioniert mit libffi, um stattdessen die unbind_variable () von bash direkt aufzurufen. Dies ist genauso schnell wie bei jeder anderen eingebauten bash:
$ readonly PI=3.14
$ unset PI
bash: unset: PI: cannot unset: readonly variable
$ source ctypes.sh
$ dlcall unbind_variable string:PI
$ declare -p PI
bash: declare: PI: not found
Zuerst müssen Sie ctypes.sh installieren:
$ git clone https://github.com/taviso/ctypes.sh.git
$ cd ctypes.sh
$ ./autogen.sh
$ ./configure
$ make
$ sudo make install
Für Neugierige können Sie auf diese Weise jede Funktion innerhalb von bash oder jede Funktion in jeder mit bash verknüpften Bibliothek oder sogar jede externe dynamisch geladene Bibliothek aufrufen, wenn Sie möchten. Bash ist jetzt genauso gefährlich wie Perl ... ;-)
Oder wie man mit variablen Metadaten spielt . Beachten Sie die Verwendung seltener Bashismen : local -n VARIABLE=$1und ${VARIABLE@a}...
destroy () {
local -n variable=$1declare -p $1 &>/dev/null || return -1 # Return if variable not existlocal reslne result flags=${variable@a}
[ -z "$flags" ] || [ "${flags//*r*}" ] && {
unset$1# Don't run gdb if variable is not readonly.return $?
}
whileread resline; do
[ "$resline" ] && [ -z "${resline%\$1 = *}" ] &&
result=${resline##*1 = }done < <(
gdb 2>&1 -ex 'call unbind_variable("'$1'")' --pid=$$ --batch
)
return$result
}
Sie könnte dies zu einem kopiert bash Quelldatei genannt destroy.bash, für die Probe ...
Erläuterung:
1 destroy () {
2 local -n variable=$1
3 declare -p $1 &>/dev/null || return -1 # Return if variable not exist
4 local reslne result flags=${variable@a}
5 [ -z "$flags" ] || [ "${flags//*r*}" ] && {
6 unset$1# Don't run gdb if variable is not readonly.
7 return $?
8 }
9 whileread resline; do
10 [ "$resline" ] && [ -z "${resline%\$1 = *}" ] &&
11 result=${resline##*1 = }
12 done < <(
13 gdb 2>&1 -ex 'call unbind_variable("'$1'")' --pid=$$ --batch
14 )
15 return$result
16 }
Zeile 2 erstellt einen lokalen Verweis auf die übermittelte Variable.
Zeile 3 verhindert das Ausführen einer nicht vorhandenen Variablen
Zeile 4 speichert die Parameterattribute (Meta) in $flags.
Die Zeilen 5 bis 8 werden ausgeführt, unsetanstatt gdbwenn kein schreibgeschütztes Flag vorhanden ist
Die Zeilen 9 bis 12 while read ... result= ... doneerhalten den Rückkehrcode call unbindin der gdbAusgabe
Zeile 13 gdbSyntax mit Verwendung von --pidund --ex(siehe gdb --help).
15 Rücklaufleitung $resultdes call unbindBefehls.
In Benutzung:
source destroy.bash
# 1st with any regular (read-write) variable: declare PI=$(bc -l <<<'4*a(1)')
echo$PI
3.14159265358979323844
echo${PI@a}# flagsdeclare -p PI
declare -- PI="3.14159265358979323844"
destroy PI
echo $?
0
declare -p PI
bash: declare: PI: not found
# now with read only variable:declare -r PI=$(bc -l <<<'4*a(1)')
declare -p PI
declare -r PI="3.14159265358979323844"echo${PI@a}# flags
r
unset PI
bash: unset: PI: cannot unset: readonly variable
destroy PI
echo $?
0
declare -p PI
bash: declare: PI: not found
# and with non existant variable
destroy PI
echo $?
255
Speziell für die TMOUT-Variable geschrieben. Eine andere Option, wenn gdb nicht verfügbar ist, besteht darin, bash in Ihr Home-Verzeichnis zu kopieren und die TMOUT-Zeichenfolge in der Binärdatei auf etwas anderes zu patchen, z. B. XMOUX. Führen Sie dann diese zusätzliche Shell-Schicht aus, und Sie erhalten keine Zeitüberschreitung.
Der Befehl readonly macht ihn endgültig und dauerhaft, bis der Shell-Prozess beendet wird. Wenn Sie eine Variable ändern müssen, markieren Sie sie nicht schreibgeschützt.
Nein, nicht in der aktuellen Shell. Wenn Sie ihm einen neuen Wert zuweisen möchten, müssen Sie eine neue Shell geben, die eine neue Bedeutung hat und nicht als solche betrachtet wird read only.
Offensichtlich würden Sie durch TMOUTdie Variable ersetzen, die Sie interessiert.
Wenn Sie das nicht selbst in ein eingebautes eingebautes Gerät verwandeln möchten, habe ich bash in GitHub gegabelt und ein vollständig geschriebenes und kompilierbares ladbares eingebautes System namens hinzugefügt readwrite. Das Commit befindet sich unter https://github.com/josephcsible/bash/commit/bcec716f4ca958e9c55a976050947d2327bcc195 . Wenn Sie es verwenden möchten, rufen Sie die Bash-Quelle mit meinem Commit ab, führen Sie sie aus ./configure && make loadables, um sie zu erstellen, enable -f examples/loadables/readwrite readwritefügen Sie sie Ihrer laufenden Sitzung hinzu und readwrite TMOUTverwenden Sie sie dann.
Sie können nicht, von der Handbuchseite von unset:
Entfernen Sie für jeden Namen die entsprechende Variable oder Funktion. Wenn keine Optionen angegeben sind oder die Option -v angegeben ist, bezieht sich jeder Name auf eine Shell-Variable. Schreibgeschützte Variablen dürfen nicht deaktiviert werden. Wenn -f angegeben wird, bezieht sich jeder Name auf eine Shell-Funktion, und die Funktionsdefinition wird entfernt. Jede nicht gesetzte Variable oder Funktion wird aus der Umgebung entfernt, die an nachfolgende Befehle übergeben wird. Wenn RANDOM, SECONDS, LINENO, HISTCMD, FUNCNAME, GROUPS oder DIRSTACK deaktiviert sind, verlieren sie ihre besonderen Eigenschaften, auch wenn sie anschließend zurückgesetzt werden. Der Exit-Status ist true, es sei denn, ein Name ist schreibgeschützt.
Was ich nicht verstehe ist, warum typeset +r VARes seitdem nicht funktioniert, auch laut Manpage,Using '+' instead of '-' turns off the attribute instead, with the exception that +a may not be used to destroy an array variable.
Trebor Rude
1
Eine andere Möglichkeit, eine schreibgeschützte Variable in Bash zu "deaktivieren", besteht darin, diese Variable in einem verfügbaren Kontext als schreibgeschützt zu deklarieren:
Während dies innerhalb des Geltungsbereichs nicht "störend" ist, was wahrscheinlich die Absicht des ursprünglichen Autors ist, setzt dies definitiv eine schreibgeschützte Variable aus der Sicht von baz () und macht sie später aus der Sicht von schreibgeschützt In Anbetracht von baz () müssen Sie Ihr Skript nur mit Bedacht schreiben.
In meinem Fall wurde eine nervige schreibgeschützte Variable eingestellt /etc/profile.d/xxx.
Zitieren des Bash-Handbuchs:
"Wenn bash als interaktive [...] Anmeldeshell aufgerufen wird, werden zuerst Befehle aus der Datei / etc / profile gelesen und ausgeführt" [...]
Wenn eine interaktive Shell gestartet wird, die keine Anmeldeshell ist, liest bash Befehle aus /etc/bash.bashrc und führt sie aus [...]
Der Kern meiner Problemumgehung bestand darin, Folgendes einzugeben ~/.bash_profile:
if [ -n "$annoying_variable" ]
thenexec env annoying_variable='' /bin/bash
# or: then exec env -i /bin/bashfi
Warnung: Um eine Rekursion zu vermeiden (die Sie sperren würde, wenn Sie nur über SSH auf Ihr Konto zugreifen können), sollten Sie sicherstellen, dass die "nervige Variable" nicht automatisch vom bashrc festgelegt wird, oder beispielsweise eine andere Variable für die Prüfung festlegen ::
readonly TMOUT
. Ich ziehe es vor, diese Zeilen zu kommentieren und eine neue Verbindung zu diesem Linux-Computer herzustellen.Antworten:
Tatsächlich können Sie eine schreibgeschützte Variable deaktivieren . aber ich muss warnen, dass dies eine hackige Methode ist. Hinzufügen dieser Antwort nur als Information, nicht als Empfehlung. Verwenden Sie es auf eigenes Risiko. Getestet auf Ubuntu 13.04, Bash 4.2.45.
Diese Methode beinhaltet das Kennen von Bash-Quellcode und er wird von dieser Antwort geerbt .
$ readonly PI=3.14 $ unset PI -bash: unset: PI: cannot unset: readonly variable $ cat << EOF| sudo gdb attach $$ call unbind_variable("PI") detach EOF $ echo $PI $
Eine Oneliner-Antwort besteht darin, den Batch-Modus und andere Befehlszeilen-Flags zu verwenden, wie in der Antwort von F. Hauri angegeben :
$ sudo gdb -ex 'call unbind_variable("PI")' --pid=$$ --batch
sudo
Möglicherweise wird es basierend auf den ptrace_scope-Einstellungen Ihres Kernels benötigt oder nicht. Weitere Informationen finden Sie in den Kommentaren zur Antwort von vip9937.quelle
cat << EOF| sudo gdb
zusudo gdb << EOF
. Es kann nicht funktionieren, da die umgeleiteten Eingang Anbieter -bash
wird aufgrund gestopptgdb
Befestigung.echo -e "attach $$\n call unbind_variable(\"PI\")\n detach" | gdb
Ich habe den obigen GDB-Hack ausprobiert, weil ich TMOUT deaktivieren möchte (um die automatische Abmeldung zu deaktivieren), aber auf dem Computer, auf dem TMOUT als schreibgeschützt festgelegt ist, darf ich sudo nicht verwenden. Aber da ich den Bash-Prozess besitze, brauche ich kein Sudo. Die Syntax funktionierte jedoch nicht ganz mit der Maschine, auf der ich mich befinde.
Dies hat jedoch funktioniert (ich habe es in meine .bashrc-Datei eingefügt):
# Disable the stupid auto-logout unset TMOUT > /dev/null 2>&1 if [ $? -ne 0 ]; then gdb <<EOF > /dev/null 2>&1 attach $$ call unbind_variable("TMOUT") detach quit EOF fi
quelle
-q -n
Optionen zu verwenden, umgdb
eine nicht geladene .gdbinit- Datei pro Sicherheit zum Schweigen zu bringen./proc/sys/kernel/yama/ptrace_scope
. Die gebräuchlichsten Werte sind0
, in welchem Fall Sie dies tun können und1
in welchem Fall Sie dies wahrscheinlich nicht können, da diesgdb
nicht das direkte übergeordnetebash
Element des zu debuggenden Prozesses ist .-q
und-n
sind hilfreich, sie (nämlich-q
) darf nicht stummgeschaltetgdb
, so dass die/dev/null
Umleitung nach wie vor erforderlich ist. Toller Vorschlag, @LucasCimonLaut Manpage:
unset [-fv] [name ...] ... Read-only variables may not be unset. ...
Wenn Sie die Variable noch nicht exportiert haben, können Sie sie
exec "$0" "$@"
zum Neustart Ihrer Shell verwenden. Natürlich verlieren Sie auch alle anderen nicht exportierten Variablen. Wenn Sie eine neue Shell ohne startenexec
, verliert sie anscheinend ihre schreibgeschützte Eigenschaft für diese Shell.quelle
Die Verwendung von GDB ist furchtbar langsam. Versuchen Sie stattdessen ctypes.sh. Es funktioniert mit libffi, um stattdessen die unbind_variable () von bash direkt aufzurufen. Dies ist genauso schnell wie bei jeder anderen eingebauten bash:
$ readonly PI=3.14 $ unset PI bash: unset: PI: cannot unset: readonly variable $ source ctypes.sh $ dlcall unbind_variable string:PI $ declare -p PI bash: declare: PI: not found
Zuerst müssen Sie ctypes.sh installieren:
$ git clone https://github.com/taviso/ctypes.sh.git $ cd ctypes.sh $ ./autogen.sh $ ./configure $ make $ sudo make install
Eine vollständige Beschreibung und Dokumente finden Sie unter https://github.com/taviso/ctypes.sh .
Für Neugierige können Sie auf diese Weise jede Funktion innerhalb von bash oder jede Funktion in jeder mit bash verknüpften Bibliothek oder sogar jede externe dynamisch geladene Bibliothek aufrufen, wenn Sie möchten. Bash ist jetzt genauso gefährlich wie Perl ... ;-)
quelle
include ctypes.sh
du meinstsource ctypes.sh
oder. ctypes.sh
.Kurz: inspiriert von Anishsanes Antwort
Aber mit einfacherer Syntax:
gdb -ex 'call unbind_variable("PI")' --pid=$$ --batch
Mit einigen Verbesserungen als Funktion:
Meine
destroy
Funktion:Oder wie man mit variablen Metadaten spielt . Beachten Sie die Verwendung seltener Bashismen :
local -n VARIABLE=$1
und${VARIABLE@a}
...destroy () { local -n variable=$1 declare -p $1 &>/dev/null || return -1 # Return if variable not exist local reslne result flags=${variable@a} [ -z "$flags" ] || [ "${flags//*r*}" ] && { unset $1 # Don't run gdb if variable is not readonly. return $? } while read resline; do [ "$resline" ] && [ -z "${resline%\$1 = *}" ] && result=${resline##*1 = } done < <( gdb 2>&1 -ex 'call unbind_variable("'$1'")' --pid=$$ --batch ) return $result }
Sie könnte dies zu einem kopiert bash Quelldatei genannt
destroy.bash
, für die Probe ...Erläuterung:
$flags
.unset
anstattgdb
wenn kein schreibgeschütztes Flag vorhanden istwhile read ... result= ... done
erhalten den Rückkehrcodecall unbind
in dergdb
Ausgabegdb
Syntax mit Verwendung von--pid
und--ex
(siehegdb --help
).$result
descall unbind
Befehls.In Benutzung:
source destroy.bash # 1st with any regular (read-write) variable: declare PI=$(bc -l <<<'4*a(1)') echo $PI 3.14159265358979323844 echo ${PI@a} # flags declare -p PI declare -- PI="3.14159265358979323844" destroy PI echo $? 0 declare -p PI bash: declare: PI: not found # now with read only variable: declare -r PI=$(bc -l <<<'4*a(1)') declare -p PI declare -r PI="3.14159265358979323844" echo ${PI@a} # flags r unset PI bash: unset: PI: cannot unset: readonly variable destroy PI echo $? 0 declare -p PI bash: declare: PI: not found # and with non existant variable destroy PI echo $? 255
quelle
In zsh,
% typeset +r PI % unset PI
(Ja, ich weiß, dass die Frage Bash lautet. Wenn Sie jedoch nach zsh googeln, erhalten Sie auch eine Reihe von Bash-Fragen.)
quelle
Speziell für die TMOUT-Variable geschrieben. Eine andere Option, wenn gdb nicht verfügbar ist, besteht darin, bash in Ihr Home-Verzeichnis zu kopieren und die TMOUT-Zeichenfolge in der Binärdatei auf etwas anderes zu patchen, z. B. XMOUX. Führen Sie dann diese zusätzliche Shell-Schicht aus, und Sie erhalten keine Zeitüberschreitung.
quelle
Der Befehl readonly macht ihn endgültig und dauerhaft, bis der Shell-Prozess beendet wird. Wenn Sie eine Variable ändern müssen, markieren Sie sie nicht schreibgeschützt.
quelle
Nein, nicht in der aktuellen Shell. Wenn Sie ihm einen neuen Wert zuweisen möchten, müssen Sie eine neue Shell geben, die eine neue Bedeutung hat und nicht als solche betrachtet wird
read only
.$ { ( readonly pi=3.14; echo $pi ); pi=400; echo $pi; unset pi; echo [$pi]; } 3.14 400 []
quelle
$ PI=3.17 $ export PI $ readonly PI $ echo $PI 3.17 $ PI=3.14 -bash: PI: readonly variable $ echo $PI 3.17
Was nun?
$ exec $BASH $ echo $PI 3.17 $ PI=3.14 $ echo $PI 3.14 $
Eine Subshell kann die Variablen der Eltern erben, erbt jedoch nicht deren geschützten Status.
quelle
Eine Alternative, wenn gdb nicht verfügbar ist: Mit dem
enable
Befehl können Sie eine benutzerdefinierte integrierte Funktion laden , mit der Sie das schreibgeschützte Attribut deaktivieren können . Der Kern des Codes, der es tut:SETVARATTR (find_variable ("TMOUT"), att_readonly, 1);
Offensichtlich würden Sie durch
TMOUT
die Variable ersetzen, die Sie interessiert.Wenn Sie das nicht selbst in ein eingebautes eingebautes Gerät verwandeln möchten, habe ich bash in GitHub gegabelt und ein vollständig geschriebenes und kompilierbares ladbares eingebautes System namens hinzugefügt
readwrite
. Das Commit befindet sich unter https://github.com/josephcsible/bash/commit/bcec716f4ca958e9c55a976050947d2327bcc195 . Wenn Sie es verwenden möchten, rufen Sie die Bash-Quelle mit meinem Commit ab, führen Sie sie aus./configure && make loadables
, um sie zu erstellen,enable -f examples/loadables/readwrite readwrite
fügen Sie sie Ihrer laufenden Sitzung hinzu undreadwrite TMOUT
verwenden Sie sie dann.quelle
Sie können nicht, von der Handbuchseite von
unset
:quelle
typeset +r VAR
es seitdem nicht funktioniert, auch laut Manpage,Using '+' instead of '-' turns off the attribute instead, with the exception that +a may not be used to destroy an array variable.
Eine andere Möglichkeit, eine schreibgeschützte Variable in Bash zu "deaktivieren", besteht darin, diese Variable in einem verfügbaren Kontext als schreibgeschützt zu deklarieren:
foo(){ declare -r PI=3.14; baz; } bar(){ local PI=3.14; baz; } baz(){ PI=3.1415927; echo PI=$PI; } foo;
Während dies innerhalb des Geltungsbereichs nicht "störend" ist, was wahrscheinlich die Absicht des ursprünglichen Autors ist, setzt dies definitiv eine schreibgeschützte Variable aus der Sicht von baz () und macht sie später aus der Sicht von schreibgeschützt In Anbetracht von baz () müssen Sie Ihr Skript nur mit Bedacht schreiben.
quelle
Eine andere Lösung ohne GDB oder eine externe Binärdatei (in der Tat ein Schwerpunkt auf Graham Nicholls Kommentar) wäre die Verwendung von
exec
.In meinem Fall wurde eine nervige schreibgeschützte Variable eingestellt
/etc/profile.d/xxx
.Zitieren des Bash-Handbuchs:
Der Kern meiner Problemumgehung bestand darin, Folgendes einzugeben
~/.bash_profile
:if [ -n "$annoying_variable" ] then exec env annoying_variable='' /bin/bash # or: then exec env -i /bin/bash fi
Warnung: Um eine Rekursion zu vermeiden (die Sie sperren würde, wenn Sie nur über SSH auf Ihr Konto zugreifen können), sollten Sie sicherstellen, dass die "nervige Variable" nicht automatisch vom bashrc festgelegt wird, oder beispielsweise eine andere Variable für die Prüfung festlegen ::
if [ -n "$annoying_variable" ] && [ "${SHLVL:-1}" = 1 ] then exec env annoying_variable='' SHLVL=$((SHLVL+1)) ${SHELL:-/bin/bash} fi
quelle