Comment étendre les alias bash

11

Comment créer un alias qui étend réellement un autre alias du même nom dans Bash?

Pourquoi:

Je l' habitude d'avoir GREP_OPTIONSmis sur .bashrcquelque chose comme ceci:

GREP_OPTIONS="-I --exclude=\*~"

J'avais aussi un script (disons, setup-java.sh) que j'appellerais avant de travailler sur certains projets Java. Il contiendrait la ligne:

GREP_OPTIONS="$GREP_OPTIONS --exclude-dir=classes"

Si j'utilise également Sass, j'appellerais setup-sass.shqui contient la ligne:

GREP_OPTIONS="$GREP_OPTIONS --exclude-dir=\*/.sass-cache"

Mais GREP_OPTIONSétait obsolète et apparemment, la solution standard est de créer un alias ou un script ...

brandizzi
la source
Qu'en est-il des fonctions bash?
Jakuje
2
Je suis tout à fait d'accord - l'utilisation d'une fonction est une bien meilleure option qu'un alias.
Charles Duffy

Réponses:

13

Bash stocke les valeurs des alias dans un tableau appelé BASH_ALIASES :

$ alias foo=bar
$ echo ${BASH_ALIASES[foo]}
bar

Avec l' expansion des paramètres, nous pouvons obtenir le dernier alias défini (s'il existe) ou la valeur par défaut:

alias grep="${BASH_ALIASES[grep]:-grep} -I --exclude=\*~"

Maintenant, faites-le sur setup-java.sh:

alias grep="${BASH_ALIASES[grep]:-grep} -I --exclude=\*~  --exclude-dir=classes"

... et enfin sur setup-sass.sh:

alias grep="${BASH_ALIASES[grep]:-grep} -I --exclude=\*~ --exclude-dir=\*/.sass-cache"

Si les trois lignes sont appelées, nous obtenons ce que nous voulons:

$ echo ${BASH_ALIASES[grep]:-grep}
grep -I --exclude=\*~ -I --exclude=\*~ --exclude-dir=classes -I --exclude=\*~ --exclude-dir=\*/.sass-cache
brandizzi
la source
13

aliases chaîne si vous les terminez par des espaces.

alias print='printf %s\\n ' hey='"hello, fine fellow" '
print hey

hello, fine fellow

Vous pouvez écrire des scripts entiers de cette façon, si vous êtes assez fou. Quoi qu'il en soit, si vous souhaitez étendre un alias, assurez-vous simplement que l'alias que vous souhaitez étendre se termine dans un espace et clouez-en un autre.

alias grep='printf "%s " -I --exclude=\*~ '    \
      exdir=' --exclude-dir=classes '          \
      exsass='--exclude-dir=\*/.sass-cache '
grep exdir exsass exdir exsass

-I --exclude=*~ --exclude-dir=classes --exclude-dir=*/.sass-cache --exclude-dir=classes --exclude-dir=*/.sass-cache
mikeserv
la source
7
C'est horriblement beau.
user1717828
Wow, c'est génial. Je ne le savais pas (et je ne l'utiliserai probablement pas beaucoup, car je pense que cela va à l'encontre de la règle de clarté ) mais c'est agréable à savoir! Question, cependant: pourquoi l'espace au début de exdir? (Est-ce juste pour l'alignement pour des raisons esthétiques?)
Wildcard
2
@Wildcard: fnmatch(){ alias fnmatch='case $1 in '; while "${1:+:}" 2>&-; do eval 'fnmatch pattern list ;; esac'; shift; done; unalias fnmatch; }; alias pattern='${1:+*}) ' list=': do stuff '; fnmatch "$@". Le faire avec aliasesvous permet d'utiliser les extensions des motifs de manière plus directe et plus sûre. Vous avez besoin d'un second contexte avec / evallorsqu'il est appelé à partir d'une fonction, mais ce n'est pas intrinsèquement dangereux tant que les noms patternet listsont contrôlés par vous. Ils ne peuvent se casser que dans la plupart des cas, même s'ils ne le sont pas, à moins qu'un attaquant ne scie correctement correctement votre case.
mikeserv
1
J'utilise ce modèle dans mon .bashrc: alias sudo='sudo ', cela me permet d' appeler toutes mes commandes crénelage après un sudo. Sans l'espace, cela ne fonctionnerait pas
arainone
1
@Wildcard - Je ne préconisais pas exactement une telle utilisation, mais c'est vrai que vous le pouvez, et il est également vrai que vous devez être au moins un peu fou pour essayer.
mikeserv
2

Une fonction est une meilleure option qu'un alias extensible ici.

grep_options=( )
grep() {
  exec /usr/bin/grep "${grep_options[@]}" ${GREP_OPTIONS} "$@"
}

De cette façon, vous avez deux options pour ajouter des options à l'environnement:

  • Modifiez le grep_optionstableau; cela prend correctement en charge les options avec des espaces, des caractères globaux littéraux et d'autres cas d'angle:

    grep_options+=( --exclude-dir=classes --exclude-dir='*/.sass-cache' )
  • Utilisez la GREP_OPTIONSvariable scalaire traditionnelle , malgré ses pièges (voir BashFAQ # 50 pour comprendre certains d'entre eux):

    GREP_OPTIONS+=' --exclude-dir=classes '

Cela dit, si vous voulez que vos options soient reflétées par des grepinstances appelées en dehors du shell, ni un alias ni une fonction ne feront l'affaire. Au lieu de cela, vous voudrez un script wrapper placé plus tôt dans votre PATH que la vraie grepcommande. Par exemple:

# in ~/.bash_profile
[[ -e ~/bin ]] && PATH=$HOME/bin:$PATH

... et, en ~/bin/grep:

#!/bin/bash

# load overrides to grep_options on GREP_OPTIONS from local dotfiles
source ~/.bash_profile
source ~/.bashrc

# ...and use them:
exec /usr/bin/grep "${grep_options[@]}" ${GREP_OPTIONS} "$@"
Charles Duffy
la source