Jak przedłużyć aliasy bash

11

Jak utworzyć alias, który faktycznie rozszerza inny alias o tej samej nazwie w Bash?

Dlaczego:

Kiedyś GREP_OPTIONSustawiłem na .bashrccoś takiego:

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

Miałem też skrypt (powiedzmy, że setup-java.sh), który zadzwoniłbym przed pracą nad niektórymi projektami Java. Zawierałby wiersz:

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

Gdybym również używał Sassa, zadzwoniłbym, setup-sass.shktóry zawiera wiersz:

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

Ale GREP_OPTIONSbył przestarzały i najwyraźniej standardowym rozwiązaniem jest albo utworzenie aliasu, albo jakiegoś skryptu ...

brandizzi
źródło
Co z funkcjami bash?
Jakuje
2
Zgadzam się - użycie funkcji jest znacznie lepszą opcją niż alias.
Charles Duffy,

Odpowiedzi:

13

Bash przechowuje wartości aliasów w tablicy o nazwie BASH_ALIASES :

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

Dzięki rozszerzeniu parametrów możemy uzyskać ostatni ustawiony alias (jeśli istnieje) lub wartość domyślną:

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

Teraz po prostu zrób to na setup-java.sh:

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

... i wreszcie setup-sass.sh:

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

Jeśli trzy linie zostaną wywołane, otrzymamy to, czego chcemy:

$ echo ${BASH_ALIASES[grep]:-grep}
grep -I --exclude=\*~ -I --exclude=\*~ --exclude-dir=classes -I --exclude=\*~ --exclude-dir=\*/.sass-cache
brandizzi
źródło
13

aliases łańcuch, jeśli zakończysz je spacjami.

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

hello, fine fellow

Możesz w ten sposób pisać całe skrypty, jeśli jesteś wystarczająco szalony. W każdym razie, jeśli chcesz przedłużyć alias, po prostu upewnij się, że alias, który chcesz przedłużyć, kończy się spacją i przypnij inny.

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
źródło
7
To jest przerażająco piękne.
user1717828,
Wow, to jest niesamowite. Nie wiedziałem o tym (i prawdopodobnie nie wykorzystam go zbyt często, ponieważ myślę, że jest to sprzeczne z regułą jasności ), ale miło jest wiedzieć! Pytanie jednak: dlaczego przestrzeń na początku exdir? (Czy to tylko ze względów estetycznych?)
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 "$@". Dzięki aliasestemu możesz używać rozszerzeń wzorów bardziej bezpośrednio i bezpieczniej. Potrzebujesz drugiego kontekstu w / evalpo wywołaniu z funkcji, ale nie jest to z natury niebezpieczne, o ile Ty patterni listnazwy są kontrolowane przez Ciebie. Mogą się złamać tylko w większości przypadków, nawet jeśli tak nie jest, chyba że jakiś atakujący świadomie poprawnie zakończy twoje case.
mikeserv
1
Używam tego wzoru w moim .bashrc: alias sudo='sudo 'ta pozwala mi nazwać wszystkie moje aliasami poleceń po sudo. Bez miejsca nie działałoby
przypadkiem
1
@Wildcard - Nie opowiadałem się za takim zastosowaniem, ale to prawda, że ​​możesz i to prawda, że ​​musisz być trochę szalony, aby spróbować.
mikeserv
2

Funkcja jest lepszą opcją niż rozszerzalny alias tutaj.

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

W ten sposób masz dwie opcje, aby dodać opcje do środowiska:

  • Zmień grep_optionstablicę; to poprawnie obsługuje opcje ze spacjami, dosłowne znaki globu i inne przypadki narożne:

    grep_options+=( --exclude-dir=classes --exclude-dir='*/.sass-cache' )
  • Użyj tradycyjnej GREP_OPTIONSzmiennej skalarnej, pomimo jej pułapek (zobacz BashFAQ # 50, aby zrozumieć niektóre z nich):

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

To powiedziawszy, jeśli chcesz, aby opcje były odzwierciedlane przez grepinstancje wywoływane poza powłoką, ani alias, ani funkcja nie będą działać. Zamiast tego będziesz chciał umieścić skrypt otoki wcześniej w swojej ŚCIEŻCE niż w prawdziwym greppoleceniu. Na przykład:

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

... oraz w ~/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
źródło