shell变量测试扩展问题

shell变量测试扩展问题

我有这个代码:

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"

好读书

关于从 shell 变量创建命令的问题的一个有趣且更普遍的讨论是:“我试图将命令放入变量中,但复杂的情况总是失败!”

答案2

POSIX 解决方案:

不加引号命令替换会受到分词的影响。

改变:

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

至(引用扩展):

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

这设置了两个参数,一个是-esed 需要的字符串,另一个是。

编辑后的脚本:

#!/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

相关内容