sed 就地命令未从 bash 中的文件中删除
sed in-place command not deleting from file in bash
我有一个 bash 脚本,它检查文件中的字符串模式并删除同一文件中的整行但不知何故它没有删除该行并且没有抛出任何错误。来自命令提示符的相同命令从文件中删除.
#array has patterns
for k in "${patternarr[@]}
do
sed -i '/$k/d' file.txt
done
sed 版本 >4
当这个循环完成时,我希望从 file.txt
中删除数组中匹配字符串模式的所有行
当我从命令提示符 运行 sed -i '/pataern/d file.txt
时它工作正常但在 bash
提前致谢
您需要在 sed 命令中使用双引号插入 shell 变量,例如:
for k in ${patternarr[@]}; do
sed -i "/$k/d" file.txt
done
这里:
sed -i '/$k/d' file.txt
sed 脚本是 单引号 ,它防止 shell 变量扩展。它将(可能)与
一起工作
sed -i "/$k/d" file.txt
我说 "probably" 是因为它将做什么取决于 $k
的内容,它只是被替换到 sed 代码中并照此解释。如果 $k
包含斜杠,它将中断。如果它来自一个不可信的来源,你就会打开代码注入的大门(特别是 GNU sed,它可以执行 shell 命令)。
考虑 k=^/ s/^/rm -Rf \//e; #
。
将 shell 变量替换为 sed 代码(或任何其他代码)通常不是一个好主意。更好的方法是使用 GNU awk:
awk -i inplace -v pattern="$k" '!([=12=] ~ pattern)' file.txt
或者只使用 grep -v
和一个临时文件。
首先,您的 for 语句中 ${patternarr[@]}
周围有一个未闭合的双引号。
那么你的问题是你在 sed 参数中使用了单引号,使得你的 shell 没有计算引号内的 $k
:
% declare -a patternarr=(foo bar fu foobar)
% for k in ${patternarr[@]}; do echo sed -i '/$k/d' file.txt; done
sed -i /$k/d file.txt
sed -i /$k/d file.txt
sed -i /$k/d file.txt
sed -i /$k/d file.txt
如果用双引号替换它们,这里是:
% for k in ${patternarr[@]}; do echo sed -i "/$k/d" file.txt; done
sed -i /foo/d file.txt
sed -i /bar/d file.txt
sed -i /fu/d file.txt
sed -i /foobar/d file.txt
任何时候你在 shell 中写一个循环只是为了操作文本,你的方法都是错误的。这可能更接近你真正应该做的(不需要周围循环):
awk -v ks="${patternarr[@]}" 'BEGIN{gsub(/ /,")|(",ks); ks="("ks")} [=10=] !~ ks' file.txt
但如果您向我们展示一些示例输入和预期输出,可能还有更好的方法(例如只检查 1 个字段而不是整行,或使用单词边界,或字符串比较或...)。
我有一个 bash 脚本,它检查文件中的字符串模式并删除同一文件中的整行但不知何故它没有删除该行并且没有抛出任何错误。来自命令提示符的相同命令从文件中删除.
#array has patterns
for k in "${patternarr[@]}
do
sed -i '/$k/d' file.txt
done
sed 版本 >4
当这个循环完成时,我希望从 file.txt
中删除数组中匹配字符串模式的所有行当我从命令提示符 运行 sed -i '/pataern/d file.txt
时它工作正常但在 bash
提前致谢
您需要在 sed 命令中使用双引号插入 shell 变量,例如:
for k in ${patternarr[@]}; do
sed -i "/$k/d" file.txt
done
这里:
sed -i '/$k/d' file.txt
sed 脚本是 单引号 ,它防止 shell 变量扩展。它将(可能)与
一起工作sed -i "/$k/d" file.txt
我说 "probably" 是因为它将做什么取决于 $k
的内容,它只是被替换到 sed 代码中并照此解释。如果 $k
包含斜杠,它将中断。如果它来自一个不可信的来源,你就会打开代码注入的大门(特别是 GNU sed,它可以执行 shell 命令)。
考虑 k=^/ s/^/rm -Rf \//e; #
。
将 shell 变量替换为 sed 代码(或任何其他代码)通常不是一个好主意。更好的方法是使用 GNU awk:
awk -i inplace -v pattern="$k" '!([=12=] ~ pattern)' file.txt
或者只使用 grep -v
和一个临时文件。
首先,您的 for 语句中 ${patternarr[@]}
周围有一个未闭合的双引号。
那么你的问题是你在 sed 参数中使用了单引号,使得你的 shell 没有计算引号内的 $k
:
% declare -a patternarr=(foo bar fu foobar)
% for k in ${patternarr[@]}; do echo sed -i '/$k/d' file.txt; done
sed -i /$k/d file.txt
sed -i /$k/d file.txt
sed -i /$k/d file.txt
sed -i /$k/d file.txt
如果用双引号替换它们,这里是:
% for k in ${patternarr[@]}; do echo sed -i "/$k/d" file.txt; done
sed -i /foo/d file.txt
sed -i /bar/d file.txt
sed -i /fu/d file.txt
sed -i /foobar/d file.txt
任何时候你在 shell 中写一个循环只是为了操作文本,你的方法都是错误的。这可能更接近你真正应该做的(不需要周围循环):
awk -v ks="${patternarr[@]}" 'BEGIN{gsub(/ /,")|(",ks); ks="("ks")} [=10=] !~ ks' file.txt
但如果您向我们展示一些示例输入和预期输出,可能还有更好的方法(例如只检查 1 个字段而不是整行,或使用单词边界,或字符串比较或...)。