ChatGPT解决这个技术问题 Extra ChatGPT

sed 中的环境变量替换

如果我从脚本运行这些命令:

#my.sh
PWD=bla
sed 's/xxx/'$PWD'/'
...
$ ./my.sh
xxx
bla

没事。

但是,如果我运行:

#my.sh
sed 's/xxx/'$PWD'/'
...
$ ./my.sh
$ sed: -e expression #1, char 8: Unknown option to `s' 

我在教程中读到,要从 shell 替换环境变量,您需要停止,并“引用” $varname 部分,以便它不会被直接替换,这就是我所做的,并且只有在立即定义变量时才有效前。

如何让 sed 将 $var 识别为环境变量,因为它在 shell 中定义?

$PWD 包含一个结束替换命令的 /。
@derobert:tnx。解决方案之一解决了这个问题......
在 shell 中使用 set -x 让 shell 在执行每个命令之前回显它们。这可以消除很多混乱。 (另外,我经常使用 set -u 使取消引用未设置的变量成为一个硬错误。(也请参见 set -e。))
我希望找到一种 sed 处理环境变量的方法,以免将值泄漏到进程表中,根据此线程中的所有答案,似乎 sed 是安装机密的错误工具

B
Benjamin W.

您的两个示例看起来相同,这使得问题难以诊断。潜在问题:

您可能需要双引号,如 sed 's/xxx/'"$PWD"'/' $PWD 可能包含斜杠,在这种情况下,您需要找到 $PWD 中未包含的字符用作分隔符。

要同时解决这两个问题,也许

sed 's@xxx@'"$PWD"'@'

但是,那么我可以使用什么字符我知道肯定不会出现在路径名中?
你可以有几个候选人,比如@#%!并使用 case 表达式检查 $PWD 是否有它们。例如,@) 的 case "$PWD" ;; *) delim="@" ;;经社理事会;重复直到 $delim 不为空。
还有另一种选择,而不是使用双引号。请参阅下面的answer
您可以为 sed 使用另一个分隔符。你不需要使用 / 你也可以使用,如果你的环境变量是一个 url。
如果字符串包含一个 \ 后跟一个 n 怎么办 - 如何阻止 sed 将其转换为单个换行符?
J
Jeach

除了 Norman Ramsey 的回答之外,我还想补充一点,您可以用双引号括起整个字符串(这可能会使语句更具可读性且不易出错)。

因此,如果要搜索 'foo' 并将其替换为 $BAR 的内容,可以将 sed 命令括在双引号中。

sed 's/foo/$BAR/g'
sed "s/foo/$BAR/g"

在第一个中,$BAR 将无法正确扩展,而在第二个中,$BAR 将正确扩展。


这比使用双引号、单引号等更干净。
这是我必须用来让环境变量在此命令中正确扩展: sed -i "s/127.0.0.1/127.0.0.1 localhost $HOSTNAME/" hosts
这很简洁,但是当您想要进行一些更复杂的替换时它不起作用,例如 "2,$s/^/$foo/" 因为 $s 也被解释为变量,它不应该。
仅此一项不起作用,因为我仍然需要像其他答案所建议的那样为路径使用不同的分隔符。
@mjp,您说的是正确的,但我认为可以将其写为 "2,$ s/^/$foo/"
B
Benjamin W.

另一个简单的选择:

由于 $PWD 通常包含斜杠 /,因此 sed 语句使用 | 而不是 /

sed -e "s|xxx|$PWD|"

您说“使用双引号的替代方法”,但您的示例使用双引号?
我想关键是它不直接在 $PWD 周围使用双引号......?
很干净的模式,但是这里不需要 -e (--expression),因为这个例子没有将几个表达式串在一起。
P
Paulo Fidalgo

您可以使用“/”以外的其他字符进行替换:

sed "s#$1#$2#g" -i FILE

在我的具体情况下,$2 是一个文件路径,因此 sed 由于解释了 $2 内容中的 / 而引起了轰动。这正是我需要克服的。感谢一个伟大的提示!
y
yurenchen

一.坏方法:更改分隔符

sed 's/xxx/'"$PWD"'/'
sed 's:xxx:'"$PWD"':'
sed 's@xxx@'"$PWD"'@'

也许那些不是最终的答案,

您无法知道 $PWD/ :@ 中会出现什么字符。
如果 $PWD 中的分隔符字符,它们会破坏表达式

好的方法是替换(转义)$PWD 中的特殊字符。

二.好方法:转义分隔符

例如:尝试将 URL 替换为 $url(内容中有 : /

x.com:80/aa/bb/aa.js

在字符串 $tmp

<a href="URL">URL</a>

A. 使用 / 作为分隔符

在 var 中将 / 转义为 \/(在 sed 表达式中使用之前)

## step 1: try escape
echo ${url//\//\\/}
x.com:80\/aa\/bb\/aa.js   #escape fine

echo ${url//\//\/}
x.com:80/aa/bb/aa.js      #escape not success

echo "${url//\//\/}"
x.com:80\/aa\/bb\/aa.js   #escape fine, notice `"`


## step 2: do sed
echo $tmp | sed "s/URL/${url//\//\\/}/"
<a href="x.com:80/aa/bb/aa.js">URL</a>

echo $tmp | sed "s/URL/${url//\//\/}/"
<a href="x.com:80/aa/bb/aa.js">URL</a>

或者

B. 使用 : 作为分隔符(比 / 更具可读性)

在 var 中将 : 转义为 \:(在 sed 表达式中使用之前)

## step 1: try escape
echo ${url//:/\:}
x.com:80/aa/bb/aa.js     #escape not success

echo "${url//:/\:}"
x.com\:80/aa/bb/aa.js    #escape fine, notice `"`


## step 2: do sed
echo $tmp | sed "s:URL:${url//:/\:}:g"
<a href="x.com:80/aa/bb/aa.js">x.com:80/aa/bb/aa.js</a>

E
Eddie

通过您的问题编辑,我看到了您的问题。假设当前目录是 /home/yourname ...在这种情况下,您的命令如下:

sed 's/xxx/'$PWD'/'

将扩大到

sed `s/xxx//home/yourname//

这是无效的。如果要执行此操作,您需要在 $PWD 中的每个 / 前面放置一个 \ 字符。


但是 PWD 是由 shell 定义的......如果我去 echo $PWD 我得到 pwd
那么,如何逃避环境变量呢?
所选答案描述了一种解决方法......不要使用斜杠作为分隔符
一旦您当前的工作目录包含其他字符,这将成为一个问题。
i
ib.

实际上,最简单的事情(至少在 GNU sed 中)是对 sed 替换 (s) 命令使用不同的分隔符。因此,不要将 s/pattern/'$mypath'/ 扩展为 s/pattern//my/path/,这当然会混淆 s 命令,而是使用 s!pattern!'$mypath'!,它将扩展为 s!pattern!/my/path!。我使用了 bang (!) 字符(或使用任何你喜欢的字符),它避免了通常的,但绝不意味着你唯一选择的正斜杠作为分隔符。


g
guido

在 sed 中处理变量

[root@gislab00207 ldom]# echo domainname: None > /tmp/1.txt

[root@gislab00207 ldom]# cat /tmp/1.txt

domainname: None

[root@gislab00207 ldom]# echo ${DOMAIN_NAME}

dcsw-79-98vm.us.oracle.com

[root@gislab00207 ldom]# cat /tmp/1.txt | sed -e 's/domainname: None/domainname: ${DOMAIN_NAME}/g'

 --- Below is the result -- very funny.

domainname: ${DOMAIN_NAME}

 --- You need to single quote your variable like this ... 

[root@gislab00207 ldom]# cat /tmp/1.txt | sed -e 's/domainname: None/domainname: '${DOMAIN_NAME}'/g'


--- The right result is below 

domainname: dcsw-79-98vm.us.oracle.com

我一直在努力让它在 Azure DevOps YAML 管道中工作。这个评论帮助我成功地使用这个技巧来标记一些配置文件。
i
ib.
VAR=8675309
echo "abcde:jhdfj$jhbsfiy/.hghi$jh:12345:dgve::" |\
sed 's/:[0-9]*:/:'$VAR':/1' 

其中 VAR 包含您要替换该字段的内容


B
Benjamin W.

我有类似的问题,我有一个列表,我必须基于模板构建一个 SQL 脚本(包含 @INPUT@ 作为要替换的元素):

for i in LIST 
do
    awk "sub(/\@INPUT\@/,\"${i}\");" template.sql >> output
done

J
Joseph Sturtevant

如果您的替换字符串可能包含其他 sed control characters,那么您可能需要两步替换(第一个 escaping the replacement string):

PWD='/a\1&b$_' # these are problematic for sed
PWD_ESC=$(printf '%s\n' "$PWD" | sed -e 's/[\/&]/\\&/g')
echo 'xxx' | sed "s/xxx/$PWD_ESC/" # now this works as expected