Linux shell:从读取另一个文件的文件中删除行

Linux shell: remove lines from an file reading another file

提问人:EricBDev 提问时间:8/11/2023 最后编辑:EricBDev 更新时间:8/17/2023 访问量:72

问:

让我们考虑 2 个文本文件,一个“main_list”和一个“ignore_list”。 对于ignore_list中的每一行,我想删除main_line中以该字符串开头的行。

基本上,使用 sed 和 while 循环是可行的。

F.D.公司

while read line; do echo ^$line; sed -i "/^$line/d" ./main_list; done < ./ignore_list

以一种更好的方式,我想首先创建 sed 模式,然后运行一次:

while read line; do
    if [ $SED_PATTERN="" ]; then 
      SED_PATTERN="^$line"
    else
      SED_PATTERN=$SED_PATTERN"\|^$line"
    fi
  done < ./ ignore_list
echo $SED_PATTERN
sed -i "/$SED_PATTERN/d" ./main_list

不幸的是,由于 while 循环使用的子 shell,它不起作用。

在 while 循环中修改的变量不会被记住https://mywiki.wooledge.org/BashFAQ/024 给出了有价值的解释和解决方法。我还没有设法让它以简单的方式工作。

理想情况下,我想使用 sh shell(该脚本将在带有简单 alpine 映像的 gitlab 管道中运行)

在我转向 python 脚本之前保持简单的任何想法(并使用胖图像而不是 alpine - 介于两者之间,我也可以将一个与 bash 一起使用)

也许是 sed 和 while 循环之外的另一种方法?

谢谢。

编辑:关于这两个文件内容的更多上下文:我正在处理从构建步骤安装的 debian 软件包列表。 然后,main_list是 dpkg-query 命令的输出(见下文),因此不应包含太花哨的字符。 ignore_list包含我想在另一个后处理步骤中忽略的包,其中包含与我的输出无关的内部组件。

以下是这两个文件的一小部分摘录

main_list

e2fsprogs|1.46.2-2|e2fsprogs|1.46.2-2
ebtables|2.0.11-4|ebtables|2.0.11-4
edgeonboarding-config|0.1|edgeonboarding-config|0.1
efibootguard|0.13+cip|efibootguard|0.13+cip
ethtool|1:5.9-1|ethtool|1:5.9-1

对于ignore_list

edgeonboarding-config

您可以在任何 Linux 系统上生成main_list,方法是运行

dpkg-query -f '${source:Package}|${source:Version}|${binary:Package}|${Version}\n' -W > main_list

对于ignore_list,只需从main_list(行的开头)中拿起几根弦

编辑2:无论如何,我最初的想法是没有必要的。 我只需要

  • 一个 sed 命令ignore_list 替换任何行$myline,并用 ^$myline|
  • 将输出设置为SED_PATTERN
  • 并设置运行另一个 sed 命令:sed -i “/$SED_PATTERN/d” ./main_list
bash sed while-loop sh 子壳

评论

1赞 KamilCuk 8/11/2023
使用 shellcheck 检查您的脚本。
1赞 Jetchisel 8/11/2023
[ $SED_PATTERN="" ]总是......true
1赞 markp-fuso 8/11/2023
请使用两个文件中的几行(包括匹配和不匹配行的混合)和预期输出来更新问题
1赞 markp-fuso 8/11/2023
您的循环不是在子 shell 中执行的while
0赞 Jetchisel 8/11/2023
另外,您还没有查看相交页面

答:

3赞 Barmar 8/11/2023 #1

您可以使用命令执行此操作。使用该选项读取要从文件中筛选出的模式列表。使用进程替换将每行的开头放入,并将其用作模式文件。grep -v-f^ignore_list

grep -v -f <(sed 's/^/^/' ignore_list) main_list > main_list.new && mv main_list.new main_list

评论

0赞 user1934428 8/11/2023
我们不知道里面会出现什么字符。如果它们是特定的正则表达式字符,则需要在 中对它们进行转义,这就是我确实会切换到 Perl 或 Ruby 或 Python 的地方。ignore_listsed
0赞 EricBDev 8/11/2023
我为我的文件添加了更多上下文。grep -v 命令似乎是不错的选择。然而,提出的解决方案尚未奏效:它只删除了ignore_list的最后一行
0赞 Barmar 8/11/2023
该文件有 CRLF 换行符的可能性吗?修复此问题ignore_listdos2unix
3赞 Ed Morton 8/11/2023 #2

根据您最近添加到问题中的输入/输出,使用任何 POSIX awk:

awk -F'|' '
    NR==FNR {
        sub(/[[:space:]]+$/,"")
        ign[$0]
        next
    }
    !($1 in ign)
' ignore_list main_list

这是仅对每行的第一个分隔字段进行字面上的完整字符串比较。|

如果您要为此使用 sed 和/或 grep,那么您需要首先转义所有可能的正则表达式元字符,请参阅 is-it-possible-to-escape-regex-metacharacters-reliable-with-sedignore_list


在您向我们展示示例输入/输出之前的原始答案:

使用任何 POSIX awk(由于未提供样本输入/输出,未经测试):

awk '
    NR==FNR {
        sub(/[[:space:]]+$/,"")
        ign[$0]
        next
    }
    {
        for ( str in ign ) {
            if ( index($0,str) == 1 ) {
                next
            }
        }
    }
' ignore_list main_list

也就是说,仅对每行的开头进行文字子字符串比较。

如果您要为此使用 sed 和/或 grep,那么您需要首先转义所有可能的正则表达式元字符,请参阅 is-it-possible-to-escape-regex-metacharacters-reliable-with-sedignore_list

评论

0赞 EricBDev 8/11/2023
谢谢,另一个受欢迎的输入。但是,由于某些原因,我的main_list中只有 2 行被删除,而我的ignore_list目前包含 21 行。
0赞 Ed Morton 8/11/2023
也许您在ignore_list行的末尾有空格。特别是,它可能具有回车符,请参阅 stackoverflow.com/questions/45772525/...。我更新了我的答案,从ignore_list行末尾剥离空格,以防万一它们存在。
0赞 EricBDev 8/11/2023
非常感谢,您使用 awk 的最后一个脚本运行良好。我只是将“ignore_list main_list”更改为“1 美元、2 美元> 3 美元”,以便使用起来更灵活。
0赞 potong 8/17/2023 #3

这可能对你有用(GNU sed):

sed 's#.*#/^&|/d#' ignore_list | sed -f - main_list

从ignore_list创建一个 sed 程序,并将其应用于main_list。

注意如果ignore_list中可能存在元字符,则需要对这些元字符进行转义。