如何在shell脚本字符串中替换变量

26

我在使用时遇到了问题...

我有一个变量,其中包含了一个带占位符的 SQL 语句:

echo $SQL
SELECT PX_PROMOTION_ID, PRIORITY, STATUS, EXCLSVE, TYPE, PERORDLMT, PERSHOPPERLMT, TOTALLMT, RSV_INT, PX_GROUP_ID, CAMPAIGN_ID, STOREENT_ID, VERSION, REVISION, EFFECTIVE, TRANSFER, CDREQUIRED, EXPIRE, LASTUPDATEBY, TO_CHAR(LASTUPDATE, 'YYYYMMDD HH24MMSS') AS LASTUPDATE, TO_CHAR(STARTDATE, 'YYYYMMDD HH24MMSS') AS STARTDATE, TO_CHAR(ENDDATE, 'YYYYMMDD HH24MMSS') AS ENDDATE, TO_CHAR(RSV_TIME, 'YYYYMMDD HH24MMSS') AS RSV_TIME, RSV_REAL, TGTSALES, NAME, CODE, RSV_VCH, OPTCOUNTER FROM PX_PROMOTION WHERE LASTUPDATE BETWEEN (SELECT MAX(BATCHSTART) FROM XRPTEBATCHCONTROL) AND TIMESTAMP('$BATCH_END')

我有另一个变量来保存这个值:

echo $BATCH_END
2012-11-14 17:06:13

我想用值替换占位符。我不太擅长Unix脚本,但是我尝试过这个:

echo $SQL | sed -e "s/'$BATCH_END/$BATCH_END/g"

但是它仍然没有被替换...

有人可以帮忙吗?我想要替换掉占位符,并将最终字符串分配给$SQL

我还需要知道如何将输出值返回到变量中,例如,我尝试过:

 SQL=`echo "$SQL" | echo "${SQL//\$BATCH_END/$BATCH_END}"`

3
回答:过多的回声。使用SQL="${SQL//\$BATCH_END/$BATCH_END}"代替。 - doubleDown
这里有一个更一般的版本的问题,涵盖了在不知道变量名称的情况下替换所有变量的内容。 - Mark Haferkamp
7个回答

49
您的脚本缺少单引号的结束符号。

请更改为:

echo $SQL | sed -e "s/'$BATCH_END/$BATCH_END/g"

至:

echo $SQL | sed -e "s/\$BATCH_END/$BATCH_END/g"

更新 - 根据后续评论:

要将上述替换的结果保存回$SQL中,请执行以下任一操作:

# Preferred way
SQL=$(echo $SQL | sed -e "s/\$BATCH_END/$BATCH_END/g")

# Old way
SQL=`echo $SQL | sed -e "s/\$BATCH_END/$BATCH_END/g"`

这被称为命令替换。两种语法($(...)与反引号括起来)都可以使用,但首选语法允许您进行嵌套。 首选的方法:Here文本 这可能比您关心的要高级一些,但按照以下方式操作将使您避免不必要地使用echo子进程:
SQL=$(sed -e "s/\$BATCH_END/$BATCH_END/g" <<< $SQL)

替换值仍需要用引号括起来。 - Jonathan Leffler
好的,我已经搞定了这个:SQL=echo $SQL | sed -e "s/$BATCH_END/$BATCH_END/g",但是赋值回去似乎不起作用,尽管SED现在看起来没问题了。我该如何将其重新赋值给SQL变量? - Richard G
@RichardG 请查看我的更新答案,针对将替换结果重新赋值回 SQL 变量,我认为这就是您所寻找的。 - sampson-chen

3

在我的终端中:

$ SQL="SELECT PX_PROMOTION_ID, PRIORITY, STATUS, EXCLSVE, TYPE, PERORDLMT, PERSHOPPERLMT, TOTALLMT, RSV_INT, PX_GROUP_ID, CAMPAIGN_ID, STOREENT_ID, VERSION, REVISION, EFFECTIVE, TRANSFER, CDREQUIRED, EXPIRE, LASTUPDATEBY, TO_CHAR(LASTUPDATE, 'YYYYMMDD HH24MMSS') AS LASTUPDATE, TO_CHAR(STARTDATE, 'YYYYMMDD HH24MMSS') AS STARTDATE, TO_CHAR(ENDDATE, 'YYYYMMDD HH24MMSS') AS ENDDATE, TO_CHAR(RSV_TIME, 'YYYYMMDD HH24MMSS') AS RSV_TIME, RSV_REAL, TGTSALES, NAME, CODE, RSV_VCH, OPTCOUNTER FROM PX_PROMOTION WHERE LASTUPDATE BETWEEN (SELECT MAX(BATCHSTART) FROM XRPTEBATCHCONTROL) AND TIMESTAMP('\$BATCH_END')"
$ # (observe: I escaped the $ sign to have the same variable as you)
$ echo "$SQL"
SELECT PX_PROMOTION_ID, PRIORITY, STATUS, EXCLSVE, TYPE, PERORDLMT, PERSHOPPERLMT, TOTALLMT, RSV_INT, PX_GROUP_ID, CAMPAIGN_ID, STOREENT_ID, VERSION, REVISION, EFFECTIVE, TRANSFER, CDREQUIRED, EXPIRE, LASTUPDATEBY, TO_CHAR(LASTUPDATE, 'YYYYMMDD HH24MMSS') AS LASTUPDATE, TO_CHAR(STARTDATE, 'YYYYMMDD HH24MMSS') AS STARTDATE, TO_CHAR(ENDDATE, 'YYYYMMDD HH24MMSS') AS ENDDATE, TO_CHAR(RSV_TIME, 'YYYYMMDD HH24MMSS') AS RSV_TIME, RSV_REAL, TGTSALES, NAME, CODE, RSV_VCH, OPTCOUNTER FROM PX_PROMOTION WHERE LASTUPDATE BETWEEN (SELECT MAX(BATCHSTART) FROM XRPTEBATCHCONTROL) AND TIMESTAMP('$BATCH_END')
$ BATCH_END="2012-11-14 17:06:13"
$ echo "$BATCH_END"
2012-11-14 17:06:13
$ # Now the replacement:
$ echo "${SQL//\$BATCH_END/$BATCH_END}"
SELECT PX_PROMOTION_ID, PRIORITY, STATUS, EXCLSVE, TYPE, PERORDLMT, PERSHOPPERLMT, TOTALLMT, RSV_INT, PX_GROUP_ID, CAMPAIGN_ID, STOREENT_ID, VERSION, REVISION, EFFECTIVE, TRANSFER, CDREQUIRED, EXPIRE, LASTUPDATEBY, TO_CHAR(LASTUPDATE, 'YYYYMMDD HH24MMSS') AS LASTUPDATE, TO_CHAR(STARTDATE, 'YYYYMMDD HH24MMSS') AS STARTDATE, TO_CHAR(ENDDATE, 'YYYYMMDD HH24MMSS') AS ENDDATE, TO_CHAR(RSV_TIME, 'YYYYMMDD HH24MMSS') AS RSV_TIME, RSV_REAL, TGTSALES, NAME, CODE, RSV_VCH, OPTCOUNTER FROM PX_PROMOTION WHERE LASTUPDATE BETWEEN (SELECT MAX(BATCHSTART) FROM XRPTEBATCHCONTROL) AND TIMESTAMP('2012-11-14 17:06:13')

完成!


好的,这是一个不错的方法,而不是SED,但是我该如何将其分配回SQL变量呢?我尝试了:SQL =echo“$ {SQL // \ $ BATCH_END / $ BATCH_END}” - Richard G
1
你应该能够像这样使用:SQL=${SQL//\$BATCH_END/$BATCH_END}。在某些shell中,您可能需要在赋值的RHS周围加上双引号,但在bash中不需要(只有bash,据我所知,支持参数扩展上的//修饰符 - 虽然发现zsh也支持这一点也不会让我感到惊讶)。 - Jonathan Leffler
非常好的列出了所有使用的命令!有助于理解正在发生的事情。 - Bruno Bieri
如何在ksh中实现相同的功能。相同的脚本报告错误的替换。 - sjd

1

您需要引用第一个$,以防止其被扩展为shell变量。

echo "$SQL" | sed -e "s/'\$BATCH_END'/'$BATCH_END'/g"

或者选择一个更简单的占位符,比如@BATCH_END@

要将结果分配回$SQL,您需要进行更多的shell转义:

SQL=`echo "$SQL" | sed -e "s/'\\\$BATCH_END'/'$BATCH_END'/g"`

1
强烈建议使用SQL=$(...)而不是SQL=`...` - Jonathan Leffler

0

一种方法是在单个参数中使用“差分引号”:

echo "$SQL" | sed -e 's/$BATCH_END/'"$BATCH_END/g"

-e选项的第一部分是用单引号括起来的,因此Shell不会扩展第一个$BATCH_END,它可以匹配SQL语句中的单词。 第二部分是用双引号括起来的,因此Shell会扩展第二个$BATCH_END,并将其文本放入SQL语句中。

如果您需要担心围绕$BATCH_END的单引号,那么您必须使用其他技巧; 可能一个反斜杠会更简单(并且它仍然是一个可行的选项):

echo "$SQL" | sed -e "s/'\$BATCH_END'/'$BATCH_END'/g"

反斜杠阻止了shell扩展第一个$BATCH_END,但没有反斜杠意味着第二个被扩展了。在双引号内,单引号失去了其“无扩展”属性。


0
问题在于您在 shell 中使用了双引号字符串。在双引号字符串中,像 $BATCH_END 这样的变量会被解释为 shell 变量并进行插值。在双引号字符串中,' 字符没有特殊含义;它不能防止变量被插值。因此,您的 $BATCH_END 字符串在两个位置都被替换;您的 sed 调用等效于:
sed -e "s/'2012-11-14 17:06:13/2012-11-14 17:06:13/"

正如您所见,这并不是很有帮助的(还有一个杂散的 ' 在那里)。您需要转义 $ 符号,以防止它被解释为 shell 变量:

sed -e "s/\$BATCH_END/$BATCH_END/"

0

使用双引号

sed -i "s/targetString/$newName/g" targetFile.toml

0

在redis.conf中添加80%内存的情况:

# GET TOTAL MEMORY
totalmemory=$(awk '{ printf "%.2f", $2/1024 ; exit}' /proc/meminfo | awk -F"." '{print $1}');

# CALCUL
ramredis=$(echo $totalmemory/100*80 | bc);

# APPLY
sed -i 's/#maxmemory/maxmemory x mb/g' /etc/redis.conf;sed -i 's/ x /'\ $ramredis'/g' /etc/redis.conf;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接