Wie deaktiviere ich eine schreibgeschützte Variable in Bash?
$ readonly PI=3.14
$ unset PI
bash: PI: readonly variable
oder ist es nicht möglich
Wie deaktiviere ich eine schreibgeschützte Variable in Bash?
$ readonly PI=3.14
$ unset PI
bash: PI: readonly variable
oder ist es nicht möglich
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.
cat << EOF| sudo gdb
zu sudo gdb << EOF
. Es kann nicht funktionieren, da die umgeleiteten Eingang Anbieter - bash
wird aufgrund gestoppt gdb
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
-q -n
Optionen zu verwenden, um gdb
eine nicht geladene .gdbinit- Datei pro Sicherheit zum Schweigen zu bringen.
/proc/sys/kernel/yama/ptrace_scope
. Die gebräuchlichsten Werte sind 0
, in welchem Fall Sie dies tun können und 1
in welchem Fall Sie dies wahrscheinlich nicht können, da dies gdb
nicht das direkte übergeordnete bash
Element des zu debuggenden Prozesses ist .
-q
und -n
sind hilfreich, sie (nämlich -q
) darf nicht stummgeschaltet gdb
, so dass die /dev/null
Umleitung nach wie vor erforderlich ist. Toller Vorschlag, @LucasCimon
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
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 ... ;-)
include ctypes.sh
du meinst source ctypes.sh
oder . ctypes.sh
.
Aber mit einfacherer Syntax:
gdb -ex 'call unbind_variable("PI")' --pid=$$ --batch
Mit einigen Verbesserungen als Funktion:
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 ...
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 while read 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 }
$flags
.unset
anstatt gdb
wenn kein schreibgeschütztes Flag vorhanden istwhile read ... result= ... done
erhalten den Rückkehrcode call unbind
in der gdb
Ausgabegdb
Syntax mit Verwendung von --pid
und --ex
(siehe gdb --help
).$result
des call unbind
Befehls.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
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.)
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
.
$ { ( readonly pi=3.14; echo $pi ); pi=400; echo $pi; unset pi; echo [$pi]; }
3.14
400
[]
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 und readwrite TMOUT
verwenden 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.
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;
bash: PI: schreibgeschützte Variable
bar;
PI = 3,1415927
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.
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:
"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" ]
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