Mam skrypt bash, który buduje wiersz polecenia w ciągu opartym na niektórych parametrach przed wykonaniem go za jednym razem. Części, które są powiązane z ciągiem poleceń, powinny być oddzielone potokami, aby ułatwić „strumieniowanie” danych przez każdy komponent.
Bardzo uproszczony przykład:
#!/bin/bash
part1=gzip -c
part2=some_other_command
cmd="cat infile"
if [ ! "$part1" = "" ]
then
cmd+=" | $part1"
fi
if [ ! "$part2" = "" ]
then
cmd+=" | $part2"
fi
cmd+="> outfile"
#show command. It looks ok
echo $cmd
#run the command. fails with pipes
$cmd
Z jakiegoś powodu rury nie działają. Kiedy uruchamiam ten skrypt, otrzymuję różne komunikaty o błędach dotyczące zazwyczaj pierwszej części polecenia (przed pierwszym potokiem).
Moje pytanie brzmi więc, czy możliwe jest zbudowanie polecenia w ten sposób i jaki jest najlepszy sposób, aby to zrobić?
infile
istnieje w bieżącym katalogu?Odpowiedzi:
Wszystko zależy od tego, kiedy wszystko zostanie ocenione. Podczas pisania
$cmd
cała reszta wiersza jest przekazywana jako argument do pierwszego słowa w$cmd
.To pokazuje, że argumentami przekazanymi do
echo
polecenia są: „/etc/passwd
”, „|
” (znak pionowego paska), „wc
” i „-l
”.Od
man bash
:źródło
Jednym z rozwiązań tego problemu na przyszłość jest użycie „eval”. Zapewnia to, że niezależnie od tego, w jaki sposób łańcuch interpretowany jest przez bash, zostaje zapomniany, a całość jest odczytywana tak, jakby była wpisana bezpośrednio w powłoce (dokładnie tego chcemy).
W powyższym przykładzie zastąpienie
z
rozwiązałem to.
źródło
eval foo "a b"
byłby taki sam jakeval foo "a" "b"
.@waltinator już wyjaśnił, dlaczego to nie działa zgodnie z oczekiwaniami. Innym sposobem jest
bash -c
wykonanie polecenia:źródło
bash -c
, aleeval
powinienem wykonać polecenie w bieżącym procesie.Być może lepszym sposobem na to jest unikanie używania
eval
i po prostu używania tablicy Bash oraz wbudowanego rozszerzenia do gromadzenia wszystkich argumentów, a następnie wykonywania ich przeciwko poleceniu.źródło