comment désactiver la variable en lecture seule dans Bash?
$ readonly PI=3.14
$ unset PI
bash: PI: readonly variable
ou est-ce pas possible?
En fait, vous pouvez supprimer une variable en lecture seule} _. mais je dois avertir que c'est une méthode hacky. Ajouter cette réponse uniquement à titre d’information et non à titre de recommandation. À utiliser à vos risques et périls. Testé sur Ubuntu 13.04, BASH 4.2.45.
Cette méthode implique de connaître un peu de code source bash et il est hérité de this answer.
$ 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
$
J'ai essayé le hack gdb ci-dessus parce que je veux désélectionner TMOUT (pour désactiver la déconnexion automatique), mais sur la machine sur laquelle TMOUT est défini en lecture seule, je ne suis pas autorisé à utiliser Sudo. Mais comme je suis propriétaire du processus bash, je n'ai pas besoin de Sudo. Cependant, la syntaxe ne fonctionnait pas vraiment avec la machine sur laquelle je suis.
Cela a fonctionné, cependant (je l'ai mis dans mon fichier .bashrc):
# 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
Selon la page de manuel:
unset [-fv] [name ...]
... Read-only variables may not be
unset. ...
Si vous n'avez pas encore exporté la variable, vous pouvez utiliser exec "$0" "$@"
pour redémarrer votre shell, mais vous perdrez également toutes les autres variables non exportées. Il semble que si vous démarrez un nouveau Shell sans exec
, il perd sa propriété en lecture seule pour ce Shell.
Utiliser GDB est terriblement lent. Essayez ctypes.sh à la place. Cela fonctionne en utilisant libffi pour appeler directement nonbind_variable () de bash, ce qui est aussi rapide que d'utiliser n'importe quel autre script intégré de 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
Vous devez d’abord installer ctypes.sh:
$ git clone https://github.com/taviso/ctypes.sh.git
$ cd ctypes.sh
$ ./autogen.sh
$ ./configure
$ make
$ Sudo make install
Voir https://github.com/taviso/ctypes.sh pour une description complète et une documentation.
Pour les curieux, oui, cela vous permet d’appeler n’importe quelle fonction dans bash, ou n’importe quelle fonction dans une bibliothèque liée à bash, ou même une bibliothèque externe chargée dynamiquement si vous le souhaitez. Bash est maintenant aussi dangereux que Perl ... ;-)
la commande readonly la rend définitive et permanente jusqu'à la fin du processus shell. Si vous devez modifier une variable, ne la marquez pas en lecture seule.
Non, pas dans le shell actuel. Si vous souhaitez lui attribuer une nouvelle valeur, vous devrez créer un nouveau shell qui aura une nouvelle signification et ne sera pas considéré comme étant read only
.
$ { ( readonly pi=3.14; echo $pi ); pi=400; echo $pi; unset pi; echo [$pi]; }
3.14
400
[]
Spécifiquement lié à la variable TMOUT. Une autre option si gdb n'est pas disponible consiste à copier bash dans votre répertoire personnel et à appliquer un correctif à la chaîne TMOUT du fichier binaire, par exemple XMOUX. Et puis exécutez cette couche supplémentaire de Shell et vous ne serez pas expiré.
Vous ne pouvez pas, à partir de la page de manuel de unset
:
Pour chaque nom, supprimez la variable ou la fonction correspondante. Si aucune option n'est fournie ou que l'option -v est indiquée, chaque nom fait référence à une variable Shell. Les variables en lecture seule peuvent ne pas être désactivées. Si -f est spécifié, chaque nom fait référence à une fonction Shell et le la définition de la fonction est supprimée. Chaque variable ou fonction non définie est supprimée de l'environnement et transmise aux commandes suivantes. Si Un des éléments suivants: RANDOM, SECONDES, LINENO, HISTCMD, FUNCNAME, GROUPES ou DIRSTACK sont non définis, ils perdent leurs propriétés spéciales, même s’ils sont ensuite réinitialisés. L'état de sortie est vrai à moins qu'un nom ne soit en lecture seule.
En zsh,
$ typeset +r PI
(Oui, je sais que la question dit bash. Mais lorsque vous utilisez Google pour zsh, vous recevez également de nombreuses questions sur bash.)
Mais avec une syntaxe plus simple:
gdb -ex 'call unbind_variable("PI")' --pid=$$ --batch
Avec quelques améliorations, en fonction:
destroy
:Ou Comment vérifier les métadonnées variables ...
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
}
Vous pouvez le copier dans un fichier source bash appelé destroy.bash
, par exemple ...
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
au lieu de gdb
si readonly flag n'est pas présentwhile read ... result= ... done
obtiennent le code retour de call unbind
dans la sortie gdb
gdb
avec utilisation de --pid
et --ex
(voir gdb --help
).$result
de la commande call unbind
.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
Une autre façon de "supprimer" une variable en lecture seule dans Bash consiste à déclarer cette variable en lecture seule dans un contexte jetable:
foo(){ declare -r PI=3.14; baz; }
bar(){ local PI=3.14; baz; }
baz(){ PI=3.1415927; echo PI=$PI; }
foo;
bash: PI: variable en lecture seule
bar;
PI = 3.1415927
Bien que ceci ne soit pas "non déterminant" dans la portée, ce qui est probablement l'intention de l'auteur original, il s'agit certainement de définir une variable en lecture seule du point de vue de baz () et de la rendre ultérieurement en lecture-écriture vue de baz (), il vous suffit d’écrire votre script avec un peu de prévoyance.
$ PI=3.17
$ export PI
$ readonly PI
$ echo $PI
3.17
$ PI=3.14
-bash: PI: readonly variable
$ echo $PI
3.17
Que faire maintenant?
$ exec $BASH
$ echo $PI
3.17
$ PI=3.14
$ echo $PI
3.14
$
Un sous-shell peut hériter des variables du parent, mais n'héritera pas de leur statut protégé.