我有两个文本文件,我想将一堆行从一个复制到另一个。文件一有一个包列表,我想将其复制到列表二。此包列表不在文件一的开头,但在列表开头有一个标记 %packages,在末尾有一个标记 %end。我想知道如何将 %packages 和 %end 之间的所有行从文件 1 复制到文件 2 中?
答案1
要将 %packages 和 %end 之间的所有行从 file1 复制到 file2:
awk '$1=="%end" {f=0;next} f{print;next} $1=="%packages" {f=1}' file1 >>file2
此解决方案旨在删除 %packages 和 %end 行。 (如果您也希望转移这些线路,下面有一个更简单的解决方案。)
由于awk
隐式循环文件中的所有行,因此上述命令将应用于file1
.它使用一个名为 的标志f
来确定我们是否在 的包部分内file1
。包部分中的每一行都会打印到 stdout,然后重定向到file2
.
让我们awk
一一考虑这些命令:
$1=="%end" {f=0;next}
此命令检查该行是否以
%end
.如果是,则该标志f
设置为零,然后我们跳到该next
行。f{print;next}
该命令检查标志是否
f
非零。如果它非零,则打印该行并跳到下一行。$1=="%packages" {f=1}
此命令检查该行是否以
%packages
.如果是,它将标志设置f
为 1,以便打印此后的行。
包括标记线:
上面不包括标记行 %packages 和 %end。如果您想要包含这些内容,请使用:
awk '/^%packages/,/^%end/ {print}' file1 >>file2
答案2
除了 awk 之外,另一个需要考虑的解决方案是 sed:
sed -n '/%packages/,/%end/ w file2' file1
按出现顺序细分:
sed
显然,其本身之后是一个开口'
。这告诉 sed 从此时开始直到结束'
是 sed 本身的参数/命令的所有内容。之后的所有内容都是输入(如果使用重定向>文件则为输出)
-n
抑制打印。如果没有它,将打印 file1 的全部内容,并打印两次匹配的文本
/pattern1/,/pattern2/
定义要匹配的范围的限制
w file
写入文件。必须是最后一个参数,后跟文件名(如果不在当前目录中,则为 /path/to/file)
最后,关闭 single 后'
我们就有了输入文件。
最后两个注意事项:
1.有些人喜欢对输入文件使用重定向,因此最终命令如下所示:
sed -n '/%packages/,/%end/ w file2' <file1
优点是更清晰 - 很明显您从哪里获得输入。同样,w file
您可以重定向输出 >file ,而不是使用:
sed -n '/%packages/,/%end/ p' <file1 >file2
在这种情况下,我们添加p
打印匹配项(覆盖 -n 进行选择)
但是,sed 可以对多个输入文件进行操作:
sed -n '/%packages/,/%end/ w file-final' file1 file2 file3
使用重定向往往会使用户忽视此功能。
2.上述匹配包括起始行和结束行,因为 sed 在行级别操作,而不是在字级别操作。一种解决方案可能是简单地通过管道传输到更多 sed:
sed -n '/%packages/,/%end/ w file2' file1 | sed -e '1d' -e '$d'
它引入了以下新功能:
-e
允许在同一输入上运行多个命令
1
显示行号匹配 工作
d
是删除匹配模式 - 第一个命令中的行号 1
$
是输入流的末尾。由于 sed 在行级别而不是单词级别上操作,因此末尾的整行都会被删除
但是,我们实际上可以在单个 sed 调用中执行此操作,使用大括号进行分组(为了清楚起见,在脚本中):
#!/bin/bash
sed -n '
/%packages/,/%end/ {
/%packages/n
/%end/ !{
w file2
}
}
' file1
这里唯一的新东西(除了分组)是使用!
来否定比赛。
/pattern/n
用图案抑制打印线(与-n
开始时相同)。
/pattern/ !
选择与模式不匹配的所有内容(反向匹配)。顺便说一句,原因很简单。如果我们采取另一个措施/%end/n
来抑制 %end 模式,我们也会抑制它限制我们的范围,并且文件将被打印到末尾。
答案3
最容易理解的:
grep -A 1000 '%packages' xx | grep -B 1000 '%end'
第一部分搜索%packages
并打印 1000 行(包括匹配的行)A 在它之后。
管道之后的第二部分:搜索%end
并打印所有 1000 行(包括匹配的行)乙 之前。
如果文件超过 1000 行,请将 1000 更改为更大的数字。
如果你想仅有的匹配行,除了搜索模式之外什么都没有,包括开始和结束正则表达式,i。 e.
grep -A 1000 '^%packages$' xx | grep -B 1000 '^%end$'
如果您不想包含匹配的行,请添加另一个管道:
grep -A 1000 '^%packages$' xx | grep -B 1000 '^%end$' | grep -v -e '^%packages$' -e '^%end$'
where-e
可用于指定多个搜索模式,并-v
用于反转匹配的含义,以选择不匹配的行。