我有这个代码:
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=pssic
、hostname=omsssi
和hostport=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"
好读书
关于从 shell 变量创建命令的问题的一个有趣且更普遍的讨论是:“我试图将命令放入变量中,但复杂的情况总是失败!”
答案2
POSIX 解决方案:
不加引号命令替换会受到分词的影响。
改变:
$( (( $compress == 1 )) && echo -n '-e /^RMTHOST/ s/$/, compress/' ) \
至(引用扩展):
-e "$( [ "$compress" -eq 1 ] && printf '%s' '/^RMTHOST/ s/$/, compress/' )" \
这设置了两个参数,一个是-e
sed 需要的字符串,另一个是。
编辑后的脚本:
#!/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
使用数组的 shell
一种完全不同的方法(也是最佳实践)是将所有参数累积为一个数组(仅限具有数组的 shell):
#!/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