web-dev-qa-db-fra.com

Variable non définie en lecture seule dans bash

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?

61
Kokizzu

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

$
83
anishsane

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
42
vip9937

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.

4
Kevin

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 ... ;-)

2
Wil

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.

2
Amit

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
[]
2
jaypal singh

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é.

1
user1089933

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.

1
Yu Hao

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.)

1
Radon Rosborough

Bref: inspiré par réponse d'Anishsane

Mais avec une syntaxe plus simple:

gdb -ex 'call unbind_variable("PI")' --pid=$$ --batch

Avec quelques améliorations, en fonction:

Ma 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 ...

Explication:

 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  }
  • ligne 2 créer une variable référence à soumise soumise pour les métadonnées
  • ligne 3 empêche l'exécution sur une variable inexistante
  • la ligne 4 stocke les attributs du paramètre dans la variable $flags.
  • les lignes 5 à 8 exécuteront unset au lieu de gdb si readonly flag n'est pas présent
  • les lignes 9 à 12 while read ... result= ... done obtiennent le code retour de call unbind dans la sortie gdb
  • ligne 13 Syntaxe gdb avec utilisation de --pid et --ex (voir gdb --help).
  • la ligne 15 retourne $result de la commande call unbind.

Utilisé:

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
1
F. Hauri

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.

1
Wil
$ 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é.

0
jezzaaaa