【问题标题】:How can I split a file up by matching lines context?如何通过匹配行上下文来拆分文件?
【发布时间】:2011-12-04 06:38:48
【问题描述】:

我有一个文件x,带有部分分隔符:

The first section

#!

The second section

#!

The third section

我想将其拆分为一系列单独的文件,例如:

The first section
#!

The second section
#!

The third section

我认为csplit 将是解决方案,命令行类似于:

$ csplit -sk x '/#!/' {9999}

但第二个文件 (xx01) 最终包含两个分隔符:

#!

The second section

#!

关于如何以符合 POSIX 的方式完成我想要的任何想法? (是的,我可以接触 Perl/Python/Ruby 和朋友;但是,重点是扩展我的 shell 知识。)


我担心我在 OSX csplit 中发现了一个错误。人们可以试一试,让我知道结果吗?

#!/bin/sh

test -e

work="$(basename $0).$RANDOM"
mkdir $work

csplit -sk -f "$work/" - '/#/' '{9999}' <<EOF
First
#
Second
#
Third
EOF

if [ $(grep -c '#' $work/01) -eq 2 ]; then
  echo FAIL Repeat
else
  echo PASS Repeat
fi

rm $work/*

csplit -sk -f "$work/" - '/#/' '/#/' <<EOF
First
#
Second
#
Third
EOF

if [ $(grep -c '#' $work/01) -eq 2 ]; then
  echo FAIL Exact
else
  echo PASS Exact
fi

uname -a

当我在我的 Snow Leopard 盒子上运行它时,我得到:

$ ./csplit-test
csplit: #: no match
FAIL Repeat
PASS Exact
Darwin lani.bigpond 11.2.0 Darwin Kernel Version 11.2.0: Tue Aug  9 20:54:00 PDT 2011; root:xnu-1699.24.8~1/RELEASE_X86_64 x86_64

在我的 Debian 机器上,我得到:

$ sh ./csplit-test 
csplit: `/#/': match not found on repetition 2
PASS Repeat
PASS Exact

【问题讨论】:

  • 使用您的确切设置对此进行测试可以得到您正在寻找的结果。我正在使用csplit (GNU coreutils) 8.5
  • 那很糟糕。我正在使用 OSX csplit。

标签: shell unix sh


【解决方案1】:

这在 LINUX 上似乎对我有用:

csplit -sk filename '/#!/' {*}

给予:

$ more xx00
The first section

$ more xx01
#!

The second section

$ more xx02
#!

The third section

您也可以使用 Ruby 或 Perl 在一个小脚本中执行此操作,并一起摆脱分隔符


在 Fedora 13 Linux 上:

$ ./test.sh 
csplit: `/#/': match not found on repetition 2
PASS Repeat
PASS Exact
Linux localhost.localdomain 2.6.34.8-68.fc13.x86_64 #1 SMP Thu Feb 17 15:03:58 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux

【讨论】:

  • 我刚刚添加了一个测试。你能运行它并告诉我结果吗?
【解决方案2】:

哦哦。 (在 Parallels VM 中运行的 FreeBSD 8.1 安装)

src ./test_split.sh
csplit: #: no match
FAIL Repeat
PASS Exact
FreeBSD <hostname> 8.1-RELEASE FreeBSD 8.1-RELEASE #0: Mon Jul 19 02:55:53 UTC 2010 root@almeida.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC i386

【讨论】:

    【解决方案3】:

    虽然不理想,但你可以用awk 做这样的事情。

    您的文件:

    [jaypal:~/Temp] cat f0
    The first section
    
    #!
    
    The second section
    
    #!
    
    The third section
    

    使用这个获取#!之前的所有内容(你可以在一个文件中重定向这个)

    [jaypal:~/Temp] awk '/#!/{exit;}1' f0 
    The first section
    

    获取#! 后跟内容并在下一个#! 之前拆分。

    [jaypal:~/Temp] awk '/^#!/{x++}{print >(x".txt")}' f0
    [jaypal:~/Temp] ls *.txt
    1.txt 2.txt
    [jaypal:~/Temp] cat 1.txt 
    #!
    
    The second section
    
    [jaypal:~/Temp] cat 2.txt 
    #!
    
    The third section
    

    你可能会通过 perl 使用类似这样的方法轻松解决问题 -

    #!/usr/bin/perl
    
    undef $/;
    $_ = <>;
    $n = 0;
    
    for $match (split(/(?=#!)/)) {
          open(O, '>temp' . ++$n);
          print O $match;
          close(O);
    }
    

    脚本创建的文件:

    [jaypal:~/Temp] cat temp1 
    The first section
    
    [jaypal:~/Temp] cat temp2 
    #!
    
    The second section
    
    [jaypal:~/Temp] cat temp3 
    #!
    
    The third section
    

    【讨论】:

      【解决方案4】:

      使用 awk 并在 linux 机器上进行测试:

      我的awk版本:

      $ awk --version | head -1
      GNU Awk 4.0.0
      

      infile 的内容

      $ cat infile
      The first section
      
      #!
      
      The second section
      
      #!
      
      The third section
      

      awk 脚本的内容:

      $ cat script.awk
      BEGIN {
              ## Set 'Input Record Separator' variable.
              RS = "#!";
      }
      
      {
              ## Set an integer variable as output file name.
              ++filenum;
      }
      
      ## For first section.
      FNR == 1 {
              ## Remove leading and trailing spaces.
              sub( /^\s+/, "", $0);
              sub( /\s+$/, "", $0);
      
              ## Print to output file.
              printf "%s\n", $0 > filenum ".txt"
      }
      
      ## For sections from second one to last one.
      FNR > 1 {
              ## Remove trailing spaces.
              sub( /\s+$/, "", $0);
      
              ## Print to output file.
              printf "%s%s\n", RS, $0 > filenum ".txt"
      }
      

      运行脚本:

      $ awk -f script.awk infile
      

      检查输出:

      $ ls [0-9].txt
      1.txt  2.txt  3.txt
      $ cat 1.txt 
      The first section
      $ cat 2.txt 
      #!
      
      The second section
      $ cat 3.txt 
      #!
      
      The third section
      

      【讨论】:

        猜你喜欢
        • 2022-08-18
        • 1970-01-01
        • 2021-12-08
        • 2016-09-09
        • 1970-01-01
        • 2019-12-31
        • 2021-07-27
        • 2022-01-14
        • 2010-12-01
        相关资源
        最近更新 更多