【问题标题】:sed is not working from inside bashsed 在 bash 内部不起作用
【发布时间】:2016-06-29 14:47:03
【问题描述】:

我正在尝试使用 sed 替换外部文件中的 ${SRC} 和 ${MSG}。它们都是在以下 bash 脚本中可用的变量。

#!/bin/bash

SRC=$1
DST=$2
MSG=$3
CONN=$4
GROUP=$5


echo "$SRC","$DST","$MSG","$CONN","$GROUP" >> /home/maaz/smpp/smppin/incoming.log

/usr/bin/sed -i -e "s/\${SRC}/$SRC/" -e "s/\${MSG}/$MSG/" request.xml >> request.xml

文件 request.xml 如下所示:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <tem:SendSMS>
        <!--Optional:-->
         <tem:Number>${SRC}</tem:Number>
         <!--Optional:-->
         <tem:AccountID>${MSG}</tem:AccountID>
      </tem:SendSMS>
   </soapenv:Body>
</soapenv:Envelope>

我需要用 bash 中的相应值替换文本 ${SRC} 和 ${MSG}。目前相同的 sed 表达式正在替换文本,如果从外部 bash 运行,但调用脚本时文件没有变化。

我们将不胜感激。

【问题讨论】:

  • 如果您使用-ised,它会自动就地编辑文件。从同时也是输入的文件重定向输出通常会给您留下一个空文件。只需从末尾删除&gt;&gt;request.xml
  • @AndrasDeak,它是 &gt;&gt; 而不是 &gt; 所以,OP 可能不会留下空文件。另外,由于这个命令不会产生任何stdout(因为-i),这可能会起作用,尽管它是错误的方式......
  • @AndrasDeak, -i 不是sed 的 POSIX 标准的一部分,并非所有操作系统和实现都提供它。此外,并非所有实现都兼容——sed -i -e 'expression' filename 适用于 GNU sed,但不适用于 MacOS sed,后者要求 -i 后跟文件扩展名以附加到备份(在 GNU 版本中是可选的)。
  • 顺便说一句 -- 使用 sed 编辑 xml 非常,非常 非常不受欢迎 -- 当您的输入文件或内容被以较小的方式替代更改。知识库中不乏关于如何正确操作的问题和答案,因此我不会过多地阐述这一点。
  • 您的脚本非常脆弱 - 例如,您需要在初始示例中引用 $1...$5,并且您需要转义您正在使用的 /在扩展替换时作为 sed 的s 命令的分隔符(例如sed -e s/'${SRC}'/"${SRC//\//\\/}"/)。当然,除了删除 -i 标志。

标签: xml bash sed


【解决方案1】:

忽略您的输入文件是 XML,envsubst 是用同名环境变量中的值替换 ${foo} 形式的占位符的正确工具。

src=$1; dst=$2; msg=$3; conn=$4; group=$5
tempfile=$(mktemp -t request.xml.XXXXXX)

# to edit request.xml in-place:
SRC=$src DST=$dst MSG=$msg CONN=$conn GROUP=$group envsubst \
  <request.xml >"$tempfile" && mv "$tempfile" request.xml

也就是说,如果我们没有忽略这一点,并选择使用支持 XML 的工具,我们会想出一个完全不同的解决方案:

#!/bin/bash
SRC=hello
MSG=world
tempfile=$(mktemp request.xml.XXXXXX)
xmlstarlet ed -u '//*[.="${SRC}"]' -v "$SRC" \
              -u '//*[.="${MSG}"]' -v "$MSG" \
  <request.xml >"$tempfile" && mv "$tempfile" request.xml

...产量,给定您的输入文件:

<?xml version="1.0"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
  <soapenv:Header/>
  <soapenv:Body>
    <tem:SendSMS>
      <!--Optional:-->
      <tem:Number>hello</tem:Number>
      <!--Optional:-->
      <tem:AccountID>world</tem:AccountID>
    </tem:SendSMS>
  </soapenv:Body>
</soapenv:Envelope>

...并且保证产生有效 XML 格式的输出,即使要替换的字符串需要转义或以其他方式修改以在 XML 中有效。

【讨论】:

  • 不错。但是有理由在您的第一种方法中导出变量吗?
  • @sjsam,否则envsubst 无法访问它们,顾名思义,它们是从环境中提取的。
  • 好的。但是在与预先存在的变量发生冲突的情况下,导出 iMHO 可能不是可取的。
  • @sjsam,如果预先存在同名的环境变量,只需设置这些同名的shell变量将按原样覆盖它们。 OP 决定使用全大写变量名的那一刻,他们使该错误不可避免。
  • @sjsam,...现在避免了错误 - 我们现在使用小写变量名,并且仅在 envsubst 命令期间导出大写变量名。
【解决方案2】:

检查系统上是否有 sed 路径 /usr/bin/sed。 这是我的/bin/sed

【讨论】:

  • 我还没有权限让 cmets 回答问题。
  • 对那个朋友感到抱歉..撤回了反对票..但是在 OP 的脚本中还有几个问题需要解决..
  • 无论如何,OP 说文件没有变化,而不是他们收到“找不到文件”错误。
【解决方案3】:

下面的脚本会做:

dst=$2 # Uppercase variables are usually reserved for the system
msg=$3 # So use lowercase variables for your scripts like 'dst','msg' and so
.
.
.
sed -E -i 's/\$\{SRC\}/'"$src"'/g;s/\$\{MSG\}/'"$msg"'/g' request.xml

备注

  1. 就地编辑选项-i 使更改在文件中生效。
  2. 你不应该这样做request.xml &gt;&gt; request.xml,即同时读取和写入文件
  3. 双引号变量没有坏处(例如SRC="$1")。这将防止变量中的分词。
  4. 带有 sed 的 -E 选项更便于移植,并且现在大多数 sed 版本都可以识别。

编辑
如果未启用扩展正则表达式,您可以编写如下 sed 语句:

sed -i 's/\${SRC}/'"$src"'/g;s/\${MSG}/'"$msg"'/g' request.xml

【讨论】:

  • 嗯? -e-E 做两件完全不同的事情;毫无疑问,一个是另一个更便携/更不便携的替代品。
  • @CharlesDuffy:我没有想到-e。事实上,当我输入那条语句时,我已经想到了-r 选项。我之所以这么说是因为并非所有 sed 手册页都列出了 E 选项,尽管它们支持它,在这种情况下,我现在觉得,不启用扩展正则表达式会更容易,因为这涉及很多转义 {} .
  • 啊。事实上,我读错了上下文:“-E 选项 [...] 更便携”要点。 (公平地说,what 它更便于移植,而不是使错误解析的可能性降低)。
猜你喜欢
  • 2017-12-24
  • 2011-03-02
  • 2011-09-04
  • 2020-04-27
  • 1970-01-01
  • 1970-01-01
  • 2014-10-19
  • 1970-01-01
相关资源
最近更新 更多