使用 sed 删除特定行的 Bash 脚本不起作用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12984215/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 03:35:20  来源:igfitidea点击:

Bash script using sed to delete specific line doesn't work

bashshellscriptingsed

提问by MFK

I am trying to write a bash script to use sed to delete some lines of a file. The line numbers are stored in another file in reverse order. The command I am trying to do is the following:

我正在尝试编写一个 bash 脚本来使用 sed 删除文件的某些行。行号以相反的顺序存储在另一个文件中。我正在尝试执行的命令如下:

sed -e '{lineNumber}d' ./file.txt

This is what I have so far but it's not working

这是我到目前为止所拥有的,但它不起作用

while read -r line 
do 
   sed -e "/${line}d" ./file.txt
done < ./lineNum.txt

I am getting the following error:
sed: -e expression #1, char 4: unterminated address regex

我收到以下错误:
sed:-e 表达式 #1,字符 4:未终止的地址正则表达式

回答by doubleDown

Actually what you did wrong is this

其实你做错的是这个

sed -e "/${line}d" ./file.txt

You see, sedhas this syntax

你看,sed有这个语法

sed -e "/REGEX/d" ./file.txt

which deletes all lines that contains match(es) to REGEXpattern. Since you have the first /, sed thinks you are trying to use regex matching, hence it's saying unterminated address regex.

它删除所有包含匹配REGEX模式的行。由于您拥有第一个/,sed 认为您正在尝试使用正则表达式匹配,因此它说unterminated address regex.

The minimal fix required is simply removing the offending backslash, i.e.

所需的最小修复只是删除有问题的反斜杠,即

sed -e "${line}d" ./file.txt


Aside: Not a sedsolution like OP requested, but does what OP wants more efficiently.

旁白:不是sed像 OP 要求的解决方案,而是更有效地执行 OP 想要的。

awk 'NR==FNR {arr[
sed 's/$/d/' linenum.txt | sed -f - file.txt
]++; next} {if (!arr[FNR]) print }' linenum.txt file.txt

回答by Jonathan Leffler

As long as there aren't outrageously many lines to be deleted and you aren't working on a system with a woefully limited version of sed(at one time, sedon HP-UX was limited to about 100 commands), then you can use:

只要没有太多要删除的行,并且您没有在具有可悲限制版本的系统上工作sed(曾一度,sed在 HP-UX 上被限制为大约 100 个命令),那么您可以使用:

 sed -f <(sed 's/$/d/' linenum.txt) file.txt

This uses the first sedto convert the line numbers into delete commands (note that part of your trouble was a stray unwanted slash) and then tells the second sedto read its script from standard input (-f -) and apply it to file.txt.

这使用第一个sed将行号转换为删除命令(请注意,您的部分麻烦是不需要的斜杠),然后告诉第二个sed从标准输入 ( -f -)读取其脚本并将其应用于file.txt.

The above works with GNU sed; it did not work with BSD sedon Mac OS X 10.7.5 (sed: -: No such file or directory). Test it before using it on your system.

以上适用于 GNU sed;它不适sed用于 Mac OS X 10.7.5 ( sed: -: No such file or directory)上的BSD 。在您的系统上使用它之前对其进行测试。

Of course, if you've got a sufficiently recent version of bash(works with bash4.2 but not with 3.2), then you can use 'process substitution' to work around the limitation of sed:

当然,如果您有足够新的版本bash(适用于bash4.2 但不适用于 3.2),那么您可以使用“进程替换”来解决以下限制sed

while read -r line; do sed -i "${line} d" ./file.txt; done < ./linenum.txt

If that doesn't work either, you can write the output of the first sedcommand to a file and then use that (temporary) file as the name for the sedscript. So, there are lots of ways to do it. However, anything over 3 processes (two runs of sedand one of rm) is extravagant. It's probably not a problem if you only need to do it once, but it could be an issue if you've got to do it many times a minute.

如果这也不起作用,您可以将第一个sed命令的输出写入文件,然后使用该(临时)文件作为sed脚本的名称。所以,有很多方法可以做到。然而,任何超过 3 个进程(两次运行sed和一次rm)都是奢侈的。如果您只需要执行一次,这可能不是问题,但如果您每分钟必须执行多次,则可能会出现问题。

回答by Johannes

sed 's/.*/&d/' lineNum.txt | sed -i -f - file.txt

This works (I think your problem was to use -e); but it's not efficient. It may be better to pass multiple lines at a time to sed, to avoid reading and writing the file once per line. E.g., you could transform linenum.txt into something like "6 d;2 d;1 d;" and then pass it to sed for one scoop processing.

这有效(我认为您的问题是使用 -e);但效率不高。一次将多行传递给 sed 可能会更好,以避免每行读取和写入一次文件。例如,您可以将 linenum.txt 转换为类似“6 d;2 d;1 d;”的内容。然后将其传递给 sed 进行一勺处理。

回答by Steve

You can make the changes directly using sedwithout using a loop:

您可以直接使用sed而不使用循环进行更改:

##代码##