построить команду путем объединения строки в bash

У меня есть скрипт 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

По какой-то причине трубы, похоже, не работают. Когда я запускаю этот скрипт, я получаю разные сообщения об ошибках, относящиеся обычно к первой части команды (перед первым каналом).

Таким образом, мой вопрос заключается в том, возможно ли построить команду таким образом, и каков наилучший способ сделать это?

13
задан 4 June 2014 в 07:31

4 ответа

Все это зависит от того, когда вещи оценены. Когда Вы тип $cmd, целый отдых строки передается как аргументы первому слову в $cmd.

walt@spong:~(0)$ a="cat /etc/passwd"
walt@spong:~(0)$ b="| wc -l"
walt@spong:~(0)$ c="$a $b"
walt@spong:~(0)$ echo $c
cat /etc/passwd | wc -l
walt@spong:~(0)$ $c
cat: invalid option -- 'l'
Try 'cat --help' for more information.
walt@spong:~(1)$ eval $c
62
walt@spong:~(0)$ a="echo /etc/passwd"
walt@spong:~(0)$ c="$a $b"
walt@spong:~(0)$ echo $c
echo /etc/passwd | wc -l
walt@spong:~(0)$ $c
/etc/passwd | wc -l
walt@spong:~(0)$ $c |od -bc
0000000 057 145 164 143 057 160 141 163 163 167 144 040 174 040 167 143  
          /   e   t   c   /   p   a   s   s   w   d       |       w   c  
0000020 040 055 154 012  
              -   l  \n  
0000024
walt@spong:~(0)$ eval $c
1  

Это показывает, что аргументы передали эти echo, команда: "/etc/passwd", "|" (вертикальный символ панели), "wc" и" -l".

От man bash:

eval [arg ...]  
    The  args  are read and concatenated together into   
    a single command.  This command is then read and  
    executed by the shell, and its exit status is returned  
    as the value of eval.  If there are no args, or only null  
    arguments, eval returns 0.
18
ответ дан 4 June 2014 в 07:31

Одно решение этого, для дальнейшего использования, состоит в том, чтобы использовать "оценку". Это гарантирует, что независимо от того, что о способе, которым строка интерпретируется ударом, забывают, и все это читается, как будто это было введено непосредственно в оболочке (который является точно, что мы хотим).

Так в примере выше, заменяя

$cmd

с

eval $cmd

решил его.

8
ответ дан 4 June 2014 в 07:31

@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
2
ответ дан 4 June 2014 в 07:31

Возможно лучший способ сделать это должно избегать использования 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
0
ответ дан 4 June 2014 в 07:31

Другие вопросы по тегам:

Похожие вопросы: