为什么这个sed命令什么都不做?

时间:2014-01-20 17:16:38

标签: bash shell sed

我试图捕捉一个文件来创建自己的副本,但同时替换一些值

我的命令是:

cat ${FILE} | sed "s|${key}|${value}|g" > ${TEMP_FILE}

然而,当我打开临时文件时,没有任何键被替换 - 只是直接交换。我已经回应了键和值的值,它们是正确的 - 它们来自数组元素。

然而,如果我使用普通字符串而不是变量,它适用于一种类型的键 - 即:

cat ${FILE} | sed "s|example_key|${value}|g" > ${TEMP_FILE}

文件中的example_key实例被替换,这就是我想要的。

但是,当我尝试使用我的数组$ key参数时,它什么也没做。不明白为什么: - (

命令用法:

declare -a props
...
....
for x in "${props[@]}"
do
    key=`echo "${x}" | cut -d '=' -f 1`
    value=`echo "${x}" | cut -d '=' -f 2`


    # global replace on the $FILE
    cat ${FILE} | sed "s|${key}|${value}|g" > ${TEMP_FILE}
    #cat ${FILE} | sed "s|example_key|${value}|g" > ${TEMP_FILE}

done

数组元素以以下格式存储:$ key = $ value

2 个答案:

答案 0 :(得分:4)

key='echo "${x}" | cut -d '=' -f 1
value='echo "${x}" | cut -d '=' -f 2

如果要进行命令替换,请使用反向标记,而不是单引号。

key=`echo "${x}" | cut -d '=' -f 1`
value=`echo "${x}" | cut -d '=' -f 2`

另请注意,当您遍历一系列key=value对时,您每次都会覆盖临时文件,只使用一个替换应用于原始文件。所以在循环结束后,最好你可以希望只会应用最后一次替换。


我还建议不要在多次传递中执行此操作 - 通过将多个表达式传递给sed来执行此操作:

for x in "${props[@]}" ; do
  subst="$subst -e 's=$x=g'"
done
sed $subst "${FILE}" > "${TEMP_FILE}"

我正在使用一个技巧,通过使用=作为sed替换表达式的分隔符,我们不必将键与值分开。命令只是变成:

sed -e 's=foo=1=g' -e 's=bar=2=g' "${FILE}" > "${TEMP_FILE}"

答案 1 :(得分:2)

感谢@BillKarwin发现问题的关键:循环的每次迭代都会消除先前迭代的替换,因为单个键值对替换的结果取代了整个输出每次都提交。

尝试以下方法:

declare -a props

# ... 

cp "$FILE" "$TEMP_FILE"
for x in "${props[@]}"; do
  IFS='=' read -r key value <<<"$x"
  sed -i '' "s|${key}|${value}|g"  "${TEMP_FILE}"
done
  • 首先将输入文件复制到输出文件,然后在循环的每次迭代中替换输出文件就地(使用sed的{​​{1}}选项)
  • 我还使用-i简化了代码,以便将每一行解析为键和值。
  • 另请注意,我一直双重引用所有变量引用。
  • @anubhava提出了一个很好的一般点:根据变量值,可能需要一个不同的正则表达式分隔符(在你的情况下:如果键或值包含'|',你就不能使用'|'来分隔正则表达式。)

更新:@BillKarwin提出了一个很好的观点:在循环中逐个执行替换是效率低下的。

这是一个完全避免循环的单行程序:

read
  • 使用sed -f <(awk -F'=' '{ if ($0) print "s/" $1 "/" substr($0, 1+length($1)) "/g" }' \ "$FILE") "$FILE" > "$TEMP_FILE" awk构建整套替换命令(每行一个)。
  • 然后通过流程替换将结果作为命令文件提供给sed sed
  • 处理嵌入-f个字符的情况。正确。