112 votes

Désactiver une variable en lecture seule en bash

Comment annuler la définition d'une variable en lecture seule dans Bash ?

$ readonly PI=3.14

$ unset PI
bash: PI: readonly variable

ou ce n'est pas possible ?

133voto

anishsane Points 5987

En fait, vous pouvez annuler une variable en lecture seule . mais je dois vous avertir qu'il s'agit d'une méthode peu pratique. J'ajoute cette réponse, uniquement à titre d'information, et non comme une recommandation. Utilisez-le à vos propres risques. Testé sur ubuntu 13.04, bash 4.2.45.

Cette méthode implique de connaître un peu le code source de bash et elle est héritée de ce réponse.

$ 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

$

Une réponse simple est d'utiliser le mode batch et d'autres drapeaux de ligne de commande, comme indiqué dans le document F. Réponse de Hauri :

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

sudo peut ou non être nécessaire en fonction des paramètres de ptrace_scope de votre noyau. Consultez les commentaires de la réponse de vip9937 pour plus de détails.

56voto

vip9937 Points 521

J'ai essayé le hack gdb ci-dessus parce que je veux désactiver TMOUT (pour désactiver la déconnexion automatique), mais sur la machine où TMOUT est en lecture seule, je ne suis pas autorisé à utiliser sudo. Mais puisque je possède le processus bash, je n'ai pas besoin de sudo. Cependant, la syntaxe ne fonctionne pas tout à fait avec la machine sur laquelle je me trouve.

Cela a pourtant fonctionné (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

16voto

F. Hauri Points 5893

En bref : inspiré par La réponse de anishsane

Edit 2021-11-10 : Ajouter (int) a _cast unbind_variable_ résultat.

Mais avec une syntaxe plus simple :

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

Avec une certaine amélioration, en tant que fonction :

Mon destroy fonction :

O Comment jouer avec les métadonnées variables . Notez l'utilisation de l'expression "rare". bashismes : local -n VARIABLE=$1 y ${VARIABLE@a} ...

destroy () { 
    declare -p $1 &>/dev/null || return -1 # Return if variable not exist
    local -n variable=$1
    local reslne result flags=${variable@a}
    [ -z "$flags" ] || [ "${flags//*r*}" ] && { 
        unset $1    # Don't run gdb if variable is not readonly.
        return $?
    }
    while read -r resline; do
        [ "$resline" ] && [ -z "${resline%%\$1 = *}" ] &&
            result=${resline##*1 = }
    done < <(
        exec gdb 2>&1 -ex 'call (int) unbind_variable("'$1'")' --pid=$$ --batch
    )
    return $result
}

Vous pouvez le copier dans un fichier source bash appelé destroy.bash pour l'échantillon...

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 (int) unbind_variable("'$1'")' --pid=$$ --batch
14      )
15      return $result
16  }
  • ligne 2 créer un local référence à la variable soumise.
  • ligne 3 empêche l'exécution sur une variable non existante
  • ligne 4 stocker les attributs du paramètre (méta) dans le fichier $flags .
  • les lignes 5 à 8 fonctionneront unset au lieu de gdb si drapeau en lecture seule pas présent
  • lignes 9 à 12 while read ... result= ... done obtenir le code de retour de call (int) unbind_variable() en gdb sortie
  • ligne 13 gdb avec l'utilisation de la syntaxe --pid y --ex (voir gdb --help ).
  • ligne 15 retour $result de unbind_variable() commandement.

En cours d'utilisation :

$ . destroy.bash
  • 1er avec toute variable régulière (lecture-écriture) :

    $ 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
  • 2ème avec variable en lecture seule :

    $ 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
  • 3ème avec une variable inexistante :

    $ destroy PI
    $ echo $?
    255

11voto

raxod502 Points 324

Dans zsh,

% typeset +r PI
% unset PI

(Oui, je sais que la question dit bash. Mais lorsque vous recherchez zsh sur Google, vous obtenez également un tas de questions sur bash).

9voto

Wil Points 51

L'utilisation de GDB est terriblement lente, ou peut même être interdite par la politique du système (c'est-à-dire qu'on ne peut pas s'attacher à un processus).

Essayez plutôt ctypes.sh. Il fonctionne en utilisant libffi pour appeler directement unbind_variable() de bash à la place, ce qui est tout aussi rapide que d'utiliser n'importe quel autre builtin 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

Véase https://github.com/taviso/ctypes.sh pour une description complète et des documents.

Pour les curieux, oui, cela vous permet d'appeler n'importe quelle fonction dans bash, ou n'importe quelle fonction dans n'importe quelle bibliothèque liée à bash, ou même n'importe quelle bibliothèque externe chargée dynamiquement si vous le souhaitez. Bash est maintenant tout aussi dangereux que perl... ;-)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X