Jak wykonać polecenie przechowywane w zmiennej?

83

Jaki jest poprawny sposób wywołania polecenia przechowywanego w zmiennej?
Czy są jakieś różnice między 1 a 2?

#!/bin/sh
cmd="ls -la $APPROOTDIR | grep exception"
#1
$cmd
#2
eval "$cmd"
Volodymyr Bezuglyy
źródło
7
Zobacz BashFAQ / 050 .
Dennis Williamson,

Odpowiedzi:

95

Powłoki uniksowe wykonują serię transformacji na każdym wierszu wejścia przed ich wykonaniem. W przypadku większości powłok wygląda to mniej więcej tak (zaczerpnięte ze strony bashpodręcznika):

  • początkowy podział na słowa
  • rozszerzenie nawiasów
  • rozszerzenie tyldy
  • interpretacja parametrów, zmiennych i wyrażeń arytmetycznych
  • podstawianie poleceń
  • wtórny podział na słowa
  • rozszerzenie ścieżki (aka globbing)
  • usunięcie wyceny

Użycie $cmdbezpośrednio powoduje zastąpienie go poleceniem podczas fazy rozwijania parametrów, a następnie przechodzi wszystkie kolejne transformacje.

Użycie eval "$cmd"nic nie robi, aż do fazy usuwania cytatów, gdzie $cmdzwracana jest taka, jaka jest i przekazywana jako parametr do eval, którego funkcją jest ponowne uruchomienie całego łańcucha przed wykonaniem.

Zasadniczo są one takie same w większości przypadków i różnią się, gdy twoje polecenie wykorzystuje kroki transformacji aż do rozwinięcia parametrów. Na przykład, używając rozwijania nawiasów:

$ cmd="echo foo{bar,baz}"
$ $cmd
foo{bar,baz}
$ eval "$cmd"
foobar foobaz
JB.
źródło
6
Jak to zrobić eval "$cmd"bez pisania eval? $($cmd)? ${$cmd}?
Steven Lu
2
@StevenLu, żadne z nich nie są równoważne - celowo tak: evaloperacja analizuje dane jako składnię; jest zatem bardzo wrażliwy na bezpieczeństwo, a robienie tego w sposób niejawny byłoby bardzo złą formą.
Charles Duffy
5

Jeśli zrobisz to, eval $cmd gdy my to zrobimy cmd="ls -l"(interaktywnie i w skrypcie), uzyskamy pożądany efekt. W twoim przypadku masz potok z grep bez wzorca, więc część grep zakończy się niepowodzeniem z komunikatem o błędzie. Po prostu $cmdwygeneruje komunikat „polecenie nie znaleziono” (lub coś podobnego). Więc spróbuj użyć eval i użyj gotowego polecenia, a nie takiego, które generuje komunikat o błędzie.

Henno Brandsma
źródło
To był po prostu błąd w druku. Powinien być „wyjątek grep”.
Volodymyr Bezuglyy
następnie użyj samego eval, a nie $ cmd, ponieważ prawdopodobnie nie zadziała (nie zadziałało w moich testach, w bash i zsh). To właśnie miał zrobić eval ...
Henno Brandsma
0

$cmdpo prostu zamieni zmienną na jej wartość do wykonania w linii poleceń. eval "$cmd"wykonuje rozwijanie zmiennych i podstawianie poleceń przed wykonaniem wynikowej wartości w wierszu poleceń

Druga metoda jest pomocna, gdy chcesz uruchamiać polecenia, które nie są elastyczne, np.
for i in {$a..$b}
pętla formatu nie zadziała, ponieważ nie zezwala na zmienne.
W takim przypadku obejściem jest potok do bash lub eval.

Testowano na Mac OSX 10.6.8, Bash 3.2.48

Zimba
źródło
-4

Myślę, że powinieneś położyć

`

(lewy przycisk) symbole wokół zmiennej.

Nickolodeon
źródło
4
Spowoduje to wykonanie wyjścia polecenia, które np. W przypadku ls -l wygeneruje komunikat typu "total" command not found "(ponieważ total ... jest częścią wyjścia ls -l, np.) Więc to jest NIE to, czego chcesz
Henno Brandsma