>我正在尝试cat一个文件以创建自己的副本,但同时替换一些值
我的命令是:
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
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}"
感谢@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
的-i
选项)。 - 我还简化了代码,使用
read
将每一行解析为键和值。 - 另请注意,我一直对所有变量引用进行双重引用。
- @anubhava提出了一个很好的一般观点:根据变量值,可能需要不同的正则表达式分隔符(在您的情况下:如果键或值包含"|",则不能使用"|"分隔正则表达式)。
更新:@BillKarwin提出了一个很好的观点:在循环中逐个执行替换是低效的。
这是一个完全避免循环的单行代码:
sed -f <(awk -F'=' '{ if ($0) print "s/" $1 "/" substr($0, 1+length($1)) "/g" }'
"$FILE") "$FILE" > "$TEMP_FILE"
- 使用
awk
为sed
构建整套替换命令(每行一个)。 - 然后通过进程替换将结果作为命令文件提供给
sed
-f
。 - 正确处理值嵌入
=
字符的情况。