提问人:EricBDev 提问时间:8/11/2023 最后编辑:EricBDev 更新时间:8/17/2023 访问量:72
Linux shell:从读取另一个文件的文件中删除行
Linux shell: remove lines from an file reading another file
问:
让我们考虑 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
答:
您可以使用命令执行此操作。使用该选项读取要从文件中筛选出的模式列表。使用进程替换将每行的开头放入,并将其用作模式文件。grep -v
-f
^
ignore_list
grep -v -f <(sed 's/^/^/' ignore_list) main_list > main_list.new && mv main_list.new main_list
评论
ignore_list
sed
ignore_list
dos2unix
根据您最近添加到问题中的输入/输出,使用任何 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-sed。ignore_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-sed。ignore_list
评论
这可能对你有用(GNU sed):
sed 's#.*#/^&|/d#' ignore_list | sed -f - main_list
从ignore_list创建一个 sed 程序,并将其应用于main_list。
注意如果ignore_list中可能存在元字符,则需要对这些元字符进行转义。
评论
[ $SED_PATTERN="" ]
总是......true
while