У меня есть сценарий bash, который строит командную строку в строке на основе некоторых параметров перед выполнением ее за один раз. Части, которые объединены в командную строку, должны быть разделены по трубам, чтобы облегчить «потоковое» передачу данных через каждый компонент.
Очень упрощенный пример:
#!/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
По какой-то причине трубы, похоже, не работают. Когда я запускаю этот скрипт, я получаю разные сообщения об ошибках, связанные обычно с первой частью команды (перед первым каналом).
Итак, мой вопрос заключается в том, можно ли построить команду таким образом , и что это лучший способ сделать это?
Одно из решений этого, для дальнейшего использования, - использовать «eval». Это гарантирует, что всякий раз, когда строка интерпретируется bash, забывается, и все это читается так, как если бы оно было напечатано непосредственно в оболочке (это именно то, что мы хотим).
Итак, в приведенном выше примере, заменив
$cmd
на
eval $cmd
.
@waltinator уже объяснил, почему это не работает так, как вы ожидали. Другой способ - использовать bash -c для выполнения вашей команды:
$ comm="cat /etc/passwd"
$ comm+="| wc -l"
$ $comm
cat: invalid option -- 'l'
Try 'cat --help' for more information.
$ bash -c "$comm"
51
Возможно, лучший способ сделать это - избегать использования eval и просто использовать массив Bash и его встроенное расширение для создания всех аргументов, а затем выполнить их по команде.
runcmd=() # This is slightly messier than declare -a but works
for cmd in $part1 $part2 $part3; do runcmd+="| $cmd "; done
cat infile ${runcmd[@]} # You might be able to do $basecmd ${runcmd[@]}
# but that sometimes requires an `eval` which isn't great