提问人:Greg'ory 提问时间:11/14/2023 最后编辑:tripleeeGreg'ory 更新时间:11/15/2023 访问量:91
SED:sh 和 bash 中的奇怪行为,zsh 中的正常行为
SED: Strange behavior in sh and bash, ok in zsh
问:
我对命令有一个奇怪的行为。sed
首先,我关心的是 Bash 脚本中有一个命令,使我能够替换位于字符串之后的字符串。sed
sas_token
文件如下:
storage_account_name = "stoftfstate"
container_name = "tfstate-ct"
key = "terraform.tfstate"
sas_token = "se=2023-11-21T09%3A51Z&sp=rwl&sip=94.233.160.130&spr=https&sv=2022-11-02&sr=c&sig=9G5tqn7RZxagUgnvpenBGm%2BNQVipRYOFxjz6l6mrvtg%3D"
然后,我生成一个新令牌,我想在上面的文件中替换该令牌。
我想使用 .
由于我生成的令牌包含 & 符号(),因此我必须使用子字符串替换,如下所示:sed
&
${NEW_TOKEN//&/\\&}
这给了我们这个命令:sed
sed -i 's/\(^sas_token =\)\(\s*.*$\)/\1 "'${NEW_TOKEN//&/\\&}'"/' .ENV/app-backend.tfvars
但它不在 or 环境中工作,只在 .但目标是仅在其中实现这一目标。bash
sh
zsh
bash
请在下面找到完整的解释:
测试方式:bash
检查口译员:
minime@yd-202203101028:~/terraform$ ps -ef | grep $$ | grep -v grep
minime 15853 3349 0 11:23 pts/2 00:00:00 bash
minime 16045 15853 0 11:25 pts/2 00:00:00 ps -ef
修改前检查文件:
minime@yd-202203101028:~/terraform$ less -F .ENV/app-backend.tfvars
storage_account_name = "stoftfstate"
container_name = "tfstate-ct"
key = "terraform.tfstate"
sas_token = "se=2023-11-21T09%3A51Z&sp=rwl&sip=94.233.160.130&spr=https&sv=2022-11-02&sr=c&sig=9G5tqn7RZxagUgnvpenBGm%2BNQVipRYOFxjz6l6mrvtg%3D"
检查新字符串以替换行:sas_token
minime@yd-202203101028:~/terraform$ echo ${TEST_STRING}
se=1998-11-21T08%3A49Z&sp=rwl&sip=193.253.170.130&spr=https&sv=2022-11-02&sr=c&sig=DE7xk1ggg%3D
运行:sed
minime@yd-202203101028:~//terraform$ sed 's/\(^sas_token =\)\(\s*.*$\)/\1 "'${TEST_STRING//&/\\&}'"/' .ENV/app-backend.tfvars
storage_account_name = "stoftfstate"
container_name = "tfstate-ct"
key = "terraform.tfstate"
sas_token = "se=2023-11-21T09%3A51Z&sp=rwl&sip=94.233.160.130&spr=https&sv=2022-11-02&sr=c&sig=9G5tqn7RZxagUgnvpenBGm%2BNQVipRYOFxjz6l6mrvtg%3D"
看?该值没有改变...sas_token
测试方式:zsh
检查口译员:
minime@yd-202203101028:~/terraform ps -ef | grep $$ | grep -v grep
mini-me 3349 3329 0 09:20 pts/2 00:00:02 /usr/bin/zsh -i
mini-me 16618 3349 0 11:42 pts/2 00:00:00 ps -ef
修改前检查文件:
minime@yd-202203101028:~/terraform less -F .ENV/app-backend.tfvars
storage_account_name = "stoftfstate"
container_name = "tfstate-ct"
key = "terraform.tfstate"
sas_token = "se=2023-11-21T09%3A51Z&sp=rwl&sip=94.233.160.130&spr=https&sv=2022-11-02&sr=c&sig=9G5tqn7RZxagUgnvpenBGm%2BNQVipRYOFxjz6l6mrvtg%3D"
检查新字符串以替换行:sas_token
minime@yd-202203101028:~/terraform echo ${TEST_STRING}
se=1998-11-21T08%3A49Z&sp=rwl&sip=193.253.170.130&spr=https&sv=2022-11-02&sr=c&sig=DE7xk1ggg%3D
运行:sed
minime@yd-202203101028:~/terraform sed 's/\(^sas_token =\)\(\s*.*$\)/\1 "'${TEST_STRING//&/\\&}'"/' .ENV/app-backend.tfvars
storage_account_name = "stoftfstate"
container_name = "tfstate-ct"
key = "terraform.tfstate"
sas_token = "se=1998-11-21T08%3A49Z&sp=rwl&sip=193.253.170.130&spr=https&sv=2022-11-02&sr=c&sig=DE7xk1ggg%3D"%
看?HAD变了!sas_token value
我缺少什么(或)?bash
sh
答:
您看到的具体问题是由于您的报价造成的。您需要(在下面添加周围空格和带引号的字符串以强调):
sed 's/foo/"' "$bar" '"/'
'<----->' "<-->" '<>'
而不是:
sed 's/foo/"' $bar '"/'
'<----->' '<>'
由于后者没有加引号,因此暴露在 shell 中进行解释,然后由您运行的 shell 以及其中设置的任何环境变量来决定如何处理它。$bar
您已经发现了一个需要转义的另一个问题,但需要转义的不仅仅是 s,其他字符甚至只是在反向引用(、等)中,请参阅是否有可能使用 sed 可靠地转义正则表达式元字符。&
&
\
/
根本问题是 sed 不理解文字字符串,但 awk 只是使用 awk 而不是尝试转义所有元字符来使 sed 的行为就像文字一样:
$ new='se=1998-11-21T08%3A49Z&sp=rwl&sip=193.253.170.130&spr=https&sv=2022-11-02&sr=c&sig=DE7xk1ggg%3D' \
awk '$1=="sas_token" { $3="\"" ENVIRON["new"] "\"" } 1' file
storage_account_name = "stoftfstate"
container_name = "tfstate-ct"
key = "terraform.tfstate"
sas_token = "se=1998-11-21T08%3A49Z&sp=rwl&sip=193.253.170.130&spr=https&sv=2022-11-02&sr=c&sig=DE7xk1ggg%3D"
$ new='~!@#$%^&*()_+=-`|}{[]\":'\'';?></.,' \
awk '$1=="sas_token" { $3="\"" ENVIRON["new"] "\"" } 1' file
storage_account_name = "stoftfstate"
container_name = "tfstate-ct"
key = "terraform.tfstate"
sas_token = "~!@#$%^&*()_+=-`|}{[]\":';?></.,"
请参阅如何在 awk 脚本中使用 shell 变量?,了解为什么我要在与 awk 脚本相同的行上指定 shell 变量值并使用(加上替代项)。ENVIRON[]
看起来这个问题与命令如何处理用特殊字符替换字符串有关,特别是 et 符号 (&)。在 Bash 中,参数扩展和转义的行为可能会有所不同,这可能是问题的原因。sed
若要解决此问题,可以尝试对命令使用以下替代语法:sed
sed -i 's/\(^sas_token =\)\(\s*.*$\)/\1 "'"$TEST_STRING"'" /' .ENV/app-backend.tfvars
此修改可确保变量正确展开并包含在命令中。它使用双引号来允许在用单引号引号引出的表达式中扩展参数。TEST_STRING
sed
sed
以下是更新的命令:
sed -i 's/\(^sas_token =\)\(\s*.*$\)/\1 "'"${TEST_STRING//&/\\&}"'" /' .ENV/app- backend.tfvars
这应该在 Bash 中起作用并保持所需的行为。
评论
TEST_STRING='foo/bar'
TEST_STRING='foo\1bar'
sed
上一个:通过终端检测相机是否正在使用中?
下一个:查找确切的单词存在
评论
$NEW_TOKEN
在任何引用之外。在 bash 中,首先扩展变量,然后拆分结果。在 zsh 中,首先对整个命令进行分词,然后对参数进行扩展。你不能指望 bash 和 zsh 的行为相同。首先确定您正在使用哪种语言,然后编写程序。编写程序并将其作为两种不同的语言运行是没有意义的。TEST_STRING=.... ; echo "your token line" | sed YOUR_SED_PARAMETERS
s/
$
...."'"${NEW_TOKEN//&/\\&}"'"....
${var}
-i