Como estender aliases do bash

11

Como criar um alias que realmente estende outro alias com o mesmo nome no Bash?

Por quê:

Eu costumava ter GREP_OPTIONSdefinido em .bashrcpara algo como isto:

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

Eu também tinha um script (digamos setup-java.sh) que eu chamaria antes de trabalhar em alguns projetos Java. Ele conteria a linha:

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

Se eu também usar Sass, eu chamaria setup-sass.shque contém a linha:

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

Mas GREP_OPTIONSfoi preterido e, aparentemente, a solução padrão é criar um alias ou algum script ...

brandizzi
fonte
E as funções do bash?
Jakuje
2
Concordo plenamente - usar uma função é uma opção muito melhor do que um apelido.
Charles Duffy

Respostas:

13

O Bash armazena os valores dos aliases em uma matriz chamada BASH_ALIASES :

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

Com a expansão de parâmetros , podemos obter o último alias definido (se existir) ou o valor padrão:

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

Agora basta fazê-lo setup-java.sh:

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

... e finalmente em setup-sass.sh:

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

Se as três linhas são chamadas, obtemos o que queremos:

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

aliases cadeia se você terminar com espaços.

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

hello, fine fellow

Você pode escrever scripts inteiros dessa maneira, se for louco o suficiente. De qualquer forma, se você deseja estender um alias, verifique se o alias que deseja estender termina em um espaço e prenda outro.

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
fonte
7
Isso é horrivelmente bonito.
precisa saber é o seguinte
Uau, isso é incrível. Não sabia disso (e provavelmente não vai usá-lo muito, porque acho que vai contra a regra da clareza ), mas é bom saber! Pergunta, no entanto: por que o espaço no início de exdir? (Isso é apenas para alinhamento por razões estéticas?) #
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 "$@". Fazer isso aliasespermite que você use as expansões dos padrões de maneira mais direta e segura. Você precisa de um segundo contexto w / evalquando chamado de dentro de uma função, mas não é inerentemente inseguro, desde que os nomes patterne listsejam controlados por você. Eles só podem quebrar na maioria dos casos, mesmo quando não estiverem, a menos que algum invasor conscientemente termine corretamente o seu case.
precisa saber é o seguinte
1
Eu uso este padrão no meu .bashrc: alias sudo='sudo ', isso deixa-me chamar todos os meus comandos alias depois de um sudo. Sem o espaço que não iria funcionar
arainone
1
@Wildcard - Eu não estava exatamente defendendo tal uso, mas é verdade que você pode, e também é verdade que você teria que ser pelo menos um pouco louco para tentar.
precisa saber é
2

Uma função é uma opção melhor do que um alias extensível aqui.

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

Dessa forma, você tem duas opções para adicionar opções ao ambiente:

  • Alterar a grep_optionsmatriz; isso suporta corretamente opções com espaços, caracteres literais glob e outros casos de canto:

    grep_options+=( --exclude-dir=classes --exclude-dir='*/.sass-cache' )
  • Use a GREP_OPTIONSvariável escalar tradicional , apesar de suas armadilhas (consulte o BashFAQ # 50 para entender algumas delas):

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

Dito isto, se você deseja que suas opções sejam refletidas por grepinstâncias invocadas fora do shell, nem um alias nem uma função servirão. Em vez disso, você desejará um script de wrapper colocado anteriormente no seu PATH do que o grepcomando real . Por exemplo:

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

... e, em ~/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
fonte