【问题标题】:pipe sed command to create multiple files管道 sed 命令创建多个文件
【发布时间】:2014-08-31 01:12:14
【问题描述】:

我需要在多次出现的文件中获取 X 到 Y,每次匹配一个出现时它都会保存到文件中。

这是一个示例文件(demo.txt):

\x00START how are you? END\x00
\x00START good thanks END\x00
sometimes random things\x00\x00 inbetween it (ignore this text)
\x00START thats nice END\x00

现在运行命令后,每个文件(/folder/demo1.txt、/folder/demo2.txt 等)除了 'START ' 但不是 'END'。

/folder/demo1.txt 应该说“开始你好吗?”,/folder/demo2.txt 应该说“开始很好,谢谢”。

所以基本上它应该管道“你好吗?”并使用 'echo' 我可以添加 'START'。

值得记住的是,我正在处理一个非常大的二进制文件。

我正在使用

sed -n -e '/\x00START/,/END\x00/ p' demo.txt > demo1.txt

但这并没有按预期工作(它在 '\x00START' 之前获取行,并且不会在第一个 'END\x00' 处停止)。

【问题讨论】:

  • 仅供参考,我认为sed 通常不能很好地处理二进制文件...
  • 啊,知道我还能用什么吗?我尝试了 grep,但这也不适用于二进制文件。
  • 这是什么文件?为什么里面有那些空字符?无论如何,二进制模式使用-b 选项(在*nix 上无关紧要),并使用\x00 匹配空字符。

标签: regex shell command-line sed


【解决方案1】:

如果你有 GNU awk,试试:

 awk -v RS='\0START|END\0' '
      length($0) {printf "START%s\n", $0 > ("folder/demo"++i".txt")}
      ' demo.txt
  • RS='\0START|END\0' 定义了一个正则表达式作为 [input] Record Separator,它将输入文件按\0STARTEND\0 之间的字符串(字节序列)分成记录(\0 代表@987654327 @(空字符。)在这里)。
    • 使用多字符、基于正则表达式的单独记录不符合 POSIX; GNU awk 支持它(就像mawk 一般,但似乎不支持NUL 字符。)。
  • 模式length($0) 确保仅当记录为非空时才执行关联的操作 ({...})。
  • {printf "START%s\n", $0 > ("folder/demo"++i)} 将每个以"START" 开头的非空记录输出到文件folder/demo{n}.txt" 中,其中{n} 表示以1 开头的序列号。

【讨论】:

  • +1 使用 RS 实现这一点,简洁的方法.. 我必须记住使用它。
  • 谢谢,@Tiago;请注意,虽然多字符,基于正则表达式的 RS 值通常在 both GNU awk mawk 中工作,在这种特殊情况下 mawk 不'不起作用 - 大概是由于 NUL 字符。
  • 现在试试,会尽快回复您。
  • @user3786834:希望它有效;请注意,如果您使用的是 Ubuntu,则可能必须将其调用为 gawk,因为默认情况下 awk 链接到 mawk
  • @user3786834:创建测试文件如下:echo $'@START how\nare you? END@@START good thanks END@sometimes random things@@ inbetween it (ignore this text@START thats nice END@' | tr @ '\0' > demo.txt.
【解决方案2】:

您可以为此使用grep

grep -Po "START\s+\K.*?(?=END)" file
how are you? 
good thanks 
thats nice 

解释:

  1. -P 允许 Perl 正则表达式
  2. -o 只提取匹配的模式
  3. -K 积极的向后看
  4. (?=something) 积极前瞻

编辑:\00 匹配为STARTEND 可能出现在两者之间:

echo -e '\00START hi how are you END\00' | grep -aPo '\00START\K.*?(?=END\00)'
 hi how are you

EDIT2: 使用 grep 的解决方案只匹配单行,对于多行最好使用 perl 代替。语法将非常相似:

echo -e '\00START hi \n how\n are\n you END\00' | perl -ne 'BEGIN{undef $/ } /\A.*?\00START\K((.|\n)*?)(?=END)/gm; print $1' 
 hi 
 how
 are
 you 

这里有什么新东西:

  1. undef $/取消定义输入分隔符$/,默认为'\n'
  2. (.|\n)*点几乎匹配任何字符,但不匹配 \n 所以我们需要在这里添加它。
  3. /gm 修饰符,g 全局 m 多行

【讨论】:

  • 感谢您的帮助,但我也需要使用十六进制,因为 'START' 和 'END' 会出现在内容的中间。
  • @user3786834 编辑了我的答案,请注意开关-a 将二进制作为文本
  • 时髦,它的工作!谢谢蒂亚戈。注意:让 grep 使用十六进制是 \x00 而不是 \00。
  • @user3786834 '\x00' 和 '\00' 对我有同样的效果。
  • 我遇到了一个问题,当'END'与内容在同一行时,它可以工作,但如果内容是多行,它就不会工作。
【解决方案3】:

我会将空值转换为换行符,以便grep 可以自行在干净的行上找到您想要的文本:

tr '\000' '\n' < yourfile.bin | grep "^START"

从那里你可以像以前一样把它带到sed

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-05
    • 2013-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多