シェル変数テスト拡張の​​問題

シェル変数テスト拡張の​​問題

私はこのコードを持っています:

sed \
$( (( $compress == 1 )) && echo -n '-e /^RMTHOST/ s/$/, compress/' ) \
-e "s|\*\*jobname\*\*|$jobname|g" \
-e "s|\*\*hostname\*\*|$hostname|g" \
-e "s|\*\*hostport\*\*|$hostport|g" \
-e "s|\*\*rmttrailname\*\*|$rmttrailname|g" < $GGPARAMSDIR/pump.template > 
$GGPARAMSDIR/$jobname.prm

これは私が望むようにほぼ動作します。その場合は、文字列を含めたいと$compress == 1思います。この場合、この部分は含まれません。sed-e /^RMTHOST/ s/$/, compress/'$compress != 1

次のエラーが発生した場合$compress is 1

 sed: -e expression #1, char 10: missing command

デバッグ用にスクリプトに set -x を追加すると、次のように拡張されます。

sed -e '/^RMTHOST/' 's/$/,' compress/ -e 's|\*\*jobname\*\*|pssic|g' -e 's|\*\*hostname\*\*|omsssi|g' -e 's|\*\*hostport\*\*|7809|g' -e 's|\*\*rmttrailname\*\*|./dirdat/dsn/rc|g'

単一のティックが-e最初の式を閉じていることに注意してください/^RMTHOST/。これが私の問題を引き起こすと確信しています。ただし、問題を解決するための構文は不明です。

ちなみに変数の値はjobname=pssichostname=omsssihostport=7809

誰でも助けることができますか?

答え1

質問

問題は、コマンド置換の結果がパス名拡張と単語分離の影響を受けることです。

コマンド置換の出力に何が起こるかを確認するために、それを使用して生成されたprintf単語を表示します。

$ printf ">%s<\n" $( (( compress == 1 )) && echo -n '-e /^RMTHOST/ s/$/, compress/' )
>-e<
>/^RMTHOST/<
>s/$/,<
>compress/<

本当に-e別の単語が必要です。ただし、単語を区切ると、sed置換コマンドがsedが実行するコマンドに分割される可能性があります。いいえ理解する:

$ sed -e '/^RMTHOST/' 's/$/,' 'compress/'
sed: -e expression #1, char 10: missing command

解決策

代わりにbash配列を試してください。

#!/bin/bash
jobname=pssic
hostname=omsssi
hostport=7809
compress=1
rmttrailname=SomethingElse

args=()
(( compress == 1 )) && args+=('-e' '/^RMTHOST/ s/$/, compress/')
args+=(
    -e "s|\*\*jobname\*\*|$jobname|g"
    -e "s|\*\*hostname\*\*|$hostname|g"
    -e "s|\*\*hostport\*\*|$hostport|g"
    -e "s|\*\*rmttrailname\*\*|$rmttrailname|g"
    )

declare -p args  # Optional: Verify the args are what we want.

sed "${args[@]}" <"$GGPARAMSDIR/pump.template" >"$GGPARAMSDIR/$jobname.prm"

よく読んだ

シェル変数からコマンドを生成する問題に関する興味深く、より一般的な議論は次のとおりです。「コマンドを変数に入れようとしていますが、複雑なケースは常に失敗します!」

答え2

POSIXソリューション:

引用なしコマンドの置換は単語の分離の影響を受けます。

変更:

$( (( $compress == 1 )) && echo -n '-e /^RMTHOST/ s/$/, compress/' ) \

受信者(引用符拡張子):

-e "$( [ "$compress" -eq 1 ] && printf '%s' '/^RMTHOST/ s/$/, compress/' )" \

これは2つのパラメータを設定します。 1つは-esedに必要な文字列、もう1つはパラメータです。

編集されたスクリプト:

#!/bin/sh

compress=$1
jobname=pssic
hostname=omsssi
hostport=7809
rmttrailname=ends
#set -x
echo "RMTHOST **jobname** **hostname** **hostport** **rmttrailname** test" | \
    sed \
    -e "$( [ "$compress" -eq 1 ] && printf '%s' '/^RMTHOST/ s/$/, compress/' )" \
    -e "s|\*\*jobname\*\*|$jobname|g" \
    -e "s|\*\*hostname\*\*|$hostname|g" \
    -e "s|\*\*hostport\*\*|$hostport|g" \
    -e "s|\*\*rmttrailname\*\*|$rmttrailname|g"
#set +x

そしてコードをテストします(なしset -x)。

$  ./so 1
RMTHOST pssic omsssi 7809 ends test, compress

set -xがコメントアウトされていない場合:

$ ./so 1
+ echo RMTHOST **jobname** **hostname** **hostport** **rmttrailname** test
+ [ 1 -eq 1 ]
+ printf %s /^RMTHOST/ s/$/, compress/
+ sed -e /^RMTHOST/ s/$/, compress/ -e s|\*\*jobname\*\*|pssic|g -e s|\*\*hostname\*\*|omsssi|g -e s|\*\*hostport\*\*|7809|g -e s|\*\*rmttrailname\*\*|ends|g
RMTHOST pssic omsssi 7809 ends test, compress
+ set +x

配列を使用するシェル

まったく異なるアプローチ(およびベストプラクティス)は、すべての引数を配列(配列を持つシェルのみ)に蓄積することです。

#!/bin/bash
compress=$1
jobname=pssic
hostname=omsssi
hostport=7809
rmttrailname=ends

args=()
(( compress == 1 )) && args+=('-e' '/^RMTHOST/ s/$/, compress/')
args+=( -e "s|\*\*jobname\*\*|$jobname|g" )
args+=( -e "s|\*\*hostname\*\*|$hostname|g" )
args+=( -e "s|\*\*hostport\*\*|$hostport|g" )
args+=( -e "s|\*\*rmttrailname\*\*|$rmttrailname|g" )

echo "RMTHOST **jobname** **hostname** **hostport** **rmttrailname** test" | \
    sed "${args[@]}"

ランニング:

$ ./script 1
RMTHOST pssic omsssi 7809 ends test, compress

関連情報