在以下配置文件中
/etc/fine-tune.conf
我们有重复的行
clean_history_in_os=true
我们要删除所有包含 clean_history_in_os=true 的行 除了文件中的第一个匹配行
到目前为止我所做的是
sed -i '/clean_history_in_os=true/d' /etc/fine-tune.conf
但问题是 sed 删除所有“clean_history_in_os=true”行
我很乐意得到解决这个问题的想法,
使用 Perl
perl -i -ne'next if /clean_history_in_os=true/ && ++$ok > 1; print' file
这会在该行上增加计数器,如果
> 1
它会跳过该行,否则打印
问题是如果我们将模式作为 shell 变量,如何将模式传递给 Perl。下面我假设 shell 变量
$VAR
包含字符串 clean_history...
在所有这些中,shell 变量的值直接用作正则表达式中的模式。如果它是问题中的文字字符串,那么下面的代码将按给定的方式进行。但是,如果可能存在特殊字符,则应将其转义;因此,在正则表达式中使用时,您可能需要在模式前面加上
\Q
。作为一般说明,应注意不要使用 shell 中的输入来运行代码(例如在 /e
下)。
将其作为参数传递,然后可以在 @ARGV
中使用perl -i -ne'
BEGIN { $qr=shift; };
next if /$qr/ && ++$ok > 1; print
' "$VAR" file
BEGIN
块 在运行时之前的 BEGIN
阶段运行(因此不适用于以下迭代)。其中,shift从@ARGV
中删除第一个元素,在上面的调用中,它是$VAR
中的值,首先由shell插值。然后文件名 file
保留在 @ARGV
中,因此可以在 -n
下进行处理(文件被打开并且其行被迭代)
-s
开关,它可以为程序启用命令行开关
perl -i -s -ne'next if /$qr/ && ++$ok > 1; print' -- -qr="$VAR" file
--
(在''
下的一行程序之后)标记程序参数的开始;然后 -qr
将变量 $qr
引入到程序中,并按上述方式为其分配值(仅使用 -qr
,变量 $qr
就会获得值 1
,因此也是一个标志)。
任何此类选项都必须位于可能的文件名之前,并且它们将从
@ARGV
中删除,以便程序可以正常处理提交的文件。
%ENV
hash 在 Perl 程序中访问该环境变量
export VAR="clean_history..."
perl -i -ne'next if /$ENV{VAR}/ && ++$ok > 1; print' file
或者,如果
$VAR
仅在此命令中使用,可以使用较短的(必须全部在一行上)
VAR="clean_history..." perl -i -ne'...' file
我宁愿推荐前两个选项中的任何一个,而不是这个。
这些是将输入传递到完全在命令行(one-liner)上输入的 Perl 程序的方法,无需
STDIN
或文件。使用脚本更好地使用库,首先Getopt::Long。
评论中给出的问题的细化指定,如果短语
clean_...
以 #
开头,则应完全跳过该行。最简单的是单独测试
next if /#$qr/; next if /$qr/ && ++$ok > 1; print
或者,依靠短路
next if /#$qr/ || (/$qr/ && ++$ok > 1); print
第一个版本不太容易出错,而且可能更清晰。
您可以使用此
awk
删除除第一行之外的所有匹配行:
awk '!(/clean_history_in_os=true/ && n++)' file
要将文件保存到位,您可以使用此
gnu awk
命令:
awk -i inplace '!(/clean_history_in_os=true/ && n++)' file
否则使用临时文件:
awk '!(/clean_history_in_os=true/ && n++)' file > $$.tmp && mv $$.tmp file
这里有一个
sed
解决方案可以实现同样的效果:
sed -i -n '0,/clean_history_in_os=true/p;/clean_history_in_os=true/!p' file