Bash中sed处理含反斜杠变量报错的原因及修复方案
sed命令报错问题排查与修复
脚本内容
source_product="\$(LOCAL_DIR)/source \\" target_product="\$(LOCAL_DIR)/target \\" echo "source_product: $source_product" echo "target_product: $target_product" echo "" echo "--- replace source with source+target ---" echo "" sed "s|${source_product}|${source_product}\n${target_product}|g" <<< "${source_product}"
实际输出
source_product: $(LOCAL_DIR)/source \ target_product: $(LOCAL_DIR)/target \ --- replace source with source+target --- sed: -e expression #1, char 70: unterminated `s' command
预期输出
source_product: $(LOCAL_DIR)/source \ target_product: $(LOCAL_DIR)/target \ --- replace source with source+target --- $(LOCAL_DIR)/source \ $(LOCAL_DIR)/target \
补充说明
现有包含$(LOCAL_DIR)/source \的文件,需在该行下方插入$(LOCAL_DIR)/target \,文件内容示例:
... other format a ... $(LOCAL_DIR)/product_foo \ $(LOCAL_DIR)/bar \ $(LOCAL_DIR)/hello \ $(LOCAL_DIR)/world \ $(LOCAL_DIR)/source \ $(LOCAL_DIR)/others_1 \ $(LOCAL_DIR)/others_2 \ $(LOCAL_DIR)/many_other_products \ ... other format b ...
倾向于使用sed作为解决方案。
问题
上述sed命令存在什么问题?该如何修复?
问题根源
sed报错的核心问题出在变量里的反斜杠干扰了sed的命令语法:
source_product末尾的\,在shell把变量展开到sed的s命令时,会转义后面的分隔符|——sed本来以为|是替换命令的结束标记,结果被反斜杠转义了,导致它认为s命令没写完,直接报“unterminated `s' command”。- 另外,直接在替换字符串里写
\n,不是所有sed版本都能识别成换行符,比如POSIX标准的sed就不认。
修复方案
原脚本的修复方式
得先把变量里的sed特殊字符(比如反斜杠、斜杠、&)转义,同时保证换行符能被正确解析:
source_product="\$(LOCAL_DIR)/source \\" target_product="\$(LOCAL_DIR)/target \\" # 转义变量里的sed特殊字符,避免搞乱命令结构 escaped_source=$(printf '%s' "$source_product" | sed 's/[\/&]/\\&/g') escaped_target=$(printf '%s' "$target_product" | sed 's/[\/&]/\\&/g') echo "source_product: $source_product" echo "target_product: $target_product" echo "" echo "--- replace source with source+target ---" echo "" # 用转义后的变量,并且用直接换行的方式实现换行(兼容所有sed版本) sed "s|${escaped_source}|${escaped_source}\ ${escaped_target}|g" <<< "${source_product}"
- 这里用
printf加sed把变量里的/、&这些sed敏感字符转义,防止破坏命令结构; - 替换内容里的换行用
\加回车的方式,不管是GNU sed还是POSIX sed都能识别。
针对实际文件插入需求的最优写法
如果是要在文件里找到$(LOCAL_DIR)/source \这一行,然后在下面插目标行,根本不用复杂的替换命令,用sed的a(追加)命令更简单靠谱:
sed '/^\$\(LOCAL_DIR\)\/source \\$/a\ $(LOCAL_DIR)/target \\' your_file.txt
- 正则
/^\$\(LOCAL_DIR\)\/source \\$/是精确匹配目标行:^和$确保整行完全匹配,不会误改其他包含该字符串的行;\(和\)转义括号,因为括号在sed正则里是特殊符号;\\$匹配行尾的反斜杠;
a\表示在匹配到的行后面追加内容,后面的行就是要插入的目标文本。
要是想直接修改原文件(原地编辑):
- GNU sed(Linux上的)加
-i参数就行:
sed -i '/^\$\(LOCAL_DIR\)\/source \\$/a\ $(LOCAL_DIR)/target \\' your_file.txt
- BSD sed(比如macOS自带的)需要给
-i加个空参数:
sed -i '' '/^\$\(LOCAL_DIR\)\/source \\$/a\ $(LOCAL_DIR)/target \\' your_file.txt
内容的提问来源于stack exchange,提问作者stackbiz
相关产品推荐
相关产品推荐

