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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13373984/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 22:57:36  来源:igfitidea点击:

how to replace a variable in shell script string

bashshell

提问by Richard G

I'm having problems getting this to work...

我在让它工作时遇到问题...

I have a variable that is holding a SQL to with a placeholder:

我有一个使用占位符保存 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')

I have another variable that holds the value:

我有另一个保存值的变量:

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

I want to replace the placeholder with the value. I'm not particularly great at Unix scripting, but I've tried this:

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

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

but it still doesn't get replaced...

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

Can anyone help? I want to replace the placeholder, and keep the final string assigned to $SQL

任何人都可以帮忙吗?我想替换占位符,并保留分配给 $SQL 的最终字符串

I also need to know how to get the value of the output back into the variable, for example, I tried:

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

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

回答by sampson-chen

You are missing the end of that single-quote pair in your script.

您在脚本中缺少该单引号对的结尾。

Change from:

更改自:

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

To:

到:

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

Updated- as per followup comment:

更新- 根据后续评论:

To save the result of the above replacement back into $SQL, do either of the following:

要将上述替换的结果保存回$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"`

This is called command substitution. Either syntax ($(...)vs. enclosure by backticks) works, but the preferred one allows you to do nesting.

这称为命令替换。任何一种语法($(...)与通过反引号括起来)都有效,但首选的语法允许您进行嵌套。

The preferred-preferred way: Herestring

首选-preferred方式:Herestring

This is probably a bit more advanced than what you care about, but doing it in the following way will save you a subprocess from having to use echounnecessarily:

这可能比您关心的要高级一些,但是以以下方式执行此操作将使您echo不必不必要地使用子流程:

SQL=$(sed -e "s/$BATCH_END/$BATCH_END/g" <<< $SQL)

回答by gniourf_gniourf

In my terminal:

在我的终端中:

$ 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')

Done!

完毕!

回答by kmkaplan

You need to quote the first $so that it does not get expanded as a shell variable.

您需要引用第一个,$以免它被扩展为 shell 变量。

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

… Or choose an easier placeholder, something like @BATCH_END@for instance.

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

To assign the result back to $SQLyou will need some more shell escaping:

要将结果分配回$SQL您将需要更多的外壳转义:

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

回答by Jonathan Leffler

One way to do it is with 'differential quoting' in a single argument:

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

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

The first part of the -eoption is in single quotes, so the shell does not expand the first $BATCH_ENDand it can match the word in the SQL statement. The second part is in double quotes, so the shell expands the second $BATCH_ENDand places its text into the SQL.

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

If you needed to worry about the single quotes around $BATCH_END, you'd have to play other tricks; probably a backslash would be simplest (and it's a viable option anyway):

如果您需要担心 周围的单引号$BATCH_END,则必须玩其他技巧;反斜杠可能是最简单的(无论如何它都是一个可行的选择):

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

The backslash stops the shell expanding the first $BATCH_ENDbut the absence of a backslash means the second is expanded. Inside the double quotes, the single quotes lose their 'no expansion' property.

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

回答by Brian Campbell

The problem is that you are using a double-quoted string in the shell. In a double quoted string, variables like $BATCH_ENDget interpreted as shell variables and interpolated. The 'character has no special meaning within a double quoted string; it doesn't prevent variables from being interpolated. So your $BATCH_ENDstring is being substituted in both places; your sedinvocation is equivalent to:

问题是您在 shell 中使用了双引号字符串。在双引号字符串中,像$BATCH_ENDget这样的变量被解释为 shell 变量并进行插值。该'字符在双引号字符串中没有特殊含义;它不会阻止变量被插入。所以你的$BATCH_END字符串在两个地方都被替换了;您的sed调用相当于:

sed -e "s/'2012-11-14 17:06:13/2012-11-14 17:06:13/"

Which as you can see, is not very helpful (you also have a stray 'in there). You need to escape the $sign, to prevent it from being interpreted as a shell variable:

正如你所看到的,这不是很有帮助(你也有一个流浪者')。您需要对$符号进行转义,以防止将其解释为 shell 变量:

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