从文件中删除确切的行(如果存在),保留其余行;错误处理 - 如何处理

从文件中删除确切的行(如果存在),保留其余行;错误处理 - 如何处理

我不太确定应该如何以良好的方式进行文本处理,所以让我问一下。

我有这个文件:~/.config/mpv/input.conf可能包含其他选项以及v disable.

如果我想从文件中删除该行,我想我可以这样做:

grep -v -q -F 'v disable' input.conf; echo $?
1

其中 - 如果文件像现在一样不包含任何其他内容,它将返回 1 (手册页1- “没有选择任何行。”),这让我很困扰,因为我无法运行简单的if-statement.

所以我必须$?在命令之后存储,然后检查它是否大于 1 是否有错误。

另外,我不清楚就地替换文件是否是一个好主意:

grep ... input.conf > input.conf

如果还有其他方法,请告诉我。另外,如果我的部分建议是正确的,请告诉我。

谢谢,干杯。


编辑1:

所需的行为是在存在该行的状态之间切换(>>在检查该行是否存在后通过简单的加法完成(删除它,因此是这个问题)。

编辑2:

接受的解决方案必须符合 POSIX 标准。不巴什主义或非 POSIX 工具。但是,您可以包含可能对其他人有用的非 POSIX 解决方案。无论如何,谢谢。

答案1

通常,grep当有一行或多行与您提供的 RE 匹配时,将返回 0(真)。该-v标志反转了这一点,因此grep将认为所有与 RE 不匹配的行都是成功的。在这种情况下,唯一一次匹配失败的情况v是源代码中只有一行并且它与您的 RE 完全对应。

因此,删除匹配行并返回已应用更改的指示会稍微复杂一些:

remove='v disable'
if grep -qxF "$remove" input.conf
then
    # Apply the removal, saving a backup
    cp -fp input.conf input.conf.bak &&
        grep -vxF "$remove" input.conf.bak >input.conf

    # Do other things that are relevant if the line was removed
else
    # Do things that are relevant if the line was not present
    :
fi

来自的替代建议瓦伦丁·巴吉拉米被做了在评论中使用ed直接就地修改文件:

if printf '%s\n' '/^v disable/d' wq | ed -s input.conf >/dev/null
then
    # Do other things that are relevant if the line was removed
    :
else
    # Do things that are relevant if the line was not present
    :
fi

请注意,在这种情况下,匹配字符串被视为 RE 而不是文字。

答案2

使用(以前称为 Perl_6)

请注意,此答案已根据@roaima 的精彩评论进行了更新。

下面的 Raku 代码告诉您在文件中找到了多少与正则表达式匹配的行。given/模式when基本上是 Raku 的“switch”(或“case”)语句。一旦找到匹配项,它就会存储在$/(或$<>) 变量中:

~$ raku -e 'my regex RE { ^^ v \s disable $$ };
            given "/path/to/original.txt".IO.lines() {
                when $_.elems  > 0 and none / <RE> / { say "no match"; };
                when $_.elems == 1 and      / <RE> / { say "one line file, with match"; };
                when $_.elems  > 1 and      / <RE> / { say "multi-line file, with match"; };
                default {say  "error"; }
            };'

因此(例如),在when上面的第三个语句中(“多行文件,带匹配”),可以将以下代码附加到该块的末尾when

#`(  
     spurt("/path/to/original_bak.txt", $_.subst(:global, "$/ "), createonly => True);
     unlink("/path/to/original.txt".IO) if "/path/to/original_bak.txt".IO  ~~ :e & :f;;
     copy("/path/to/original_bak.txt", "/path/to/original.txt", createonly => True)
  )  

上述三个文件操作的错误处理是内置的:请参阅,类 X::IO::Unlink, 和类 X::IO::复制。你可以加文件测试操作员语句来测试更多方面。

spurt语句写入一个新_bak文件,unlink取消原始文件的链接,并将备份文件copy复制_bak到原始名称,留下两个文件。删除多行注释指示符(第一行和最后一行),并在您满意代码正常工作(根据文本返回值判断)后将两个实例更改createonly为。False

你可以做类似的事情“一行文件,匹配”case,或者只是更改条件以将两个when语句合并为一个。如果您不喜欢单行格式,您可以另存为脚本、使其可执行、作为 cron 作业运行等。


以下是之前的答案(Raku 和 Perl),它们不进行错误处理:

乐: ~$ raku -ne '.put unless /^v\sdisable$/;' input.conf > tmp_input.conf

珀尔: ~$ perl -ne 'print unless /^v disable$/;' input.conf > tmp_input.conf

这些答案与@Panki 发布的答案非常接近sed,优点是 Perl/Raku 将删除创建的空行。有关 Perl/Raku 差异的说明,请参阅下面的第二个链接。

Perl可以进行-i“就地”替换;拉库不能。但您可以修改下面的第三个链接,以使用 shell 重定向来规避 Raku 限制(无论如何,请先进行备份!)。


注意:Raku 不符合 POSIX 标准,正如 OP 在编辑中所要求的那样。我将这个答案留给任何寻求非 POSIX 方法的人。

https://docs.raku.org/language/5to6-nutshell#given-when https://unix.stackexchange.com/a/749407/227738
https://unix.stackexchange.com/a/204378/227738
https://raku.org

相关内容