一度に実行する前に、いくつかのパラメーターに基づいて文字列にコマンドラインを作成する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
何らかの理由で、パイプが機能していないようです。このスクリプトを実行すると、通常はコマンドの最初の部分(最初のパイプの前)に関連するさまざまなエラーメッセージが表示されます。
だから私の質問は、この方法でコマンドを作成することが可能かどうか、そしてそれを行うための最良の方法は何ですか?
それはすべて物事が評価される時期に依存します。 $cmd
と入力すると、行全体が$cmd
の最初のWordに引数として渡されます。
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.
これに対する解決策の1つは、将来の参照のために、「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