【问题标题】:Split a big txt file to do grep - unix拆分一个大的 txt 文件来做 grep - unix
【发布时间】:2014-05-26 13:00:16
【问题描述】:

我使用 txt 文件(unix、shell 脚本),这些文件是由管道分隔的数百万字段,而不是由 \n\r 分隔的。 像这样:

field1a|field2a|field3a|field4a|field5a|field6a|[...]|field1d|field2d|field3d|field4d|field5d|field6d|[...]|field1m|field2m|field3m|field4m|field5m|field6m|[...]|field1z|field2z|field3z|field4z|field5z|field6z|

所有文本都在同一行。

每个文件的字段数是固定的。

(在这个例子中我有field1=name; field2=surname; field3=mobile phone; field4=email; field5=office phone; field6=skype

当我需要查找字段(例如 field2)时,像 grep 这样的命令不起作用(在同一行中)。

我认为一个好的解决方案是编写一个脚本,用“\n”分割每 6 个字段,然后执行 grep。我是正确的?非常感谢!

【问题讨论】:

  • 只是想知道,为什么你的文本文件是这样构建的?
  • 为什么有人会否决这个问题?因为OP必须以愚蠢的格式处理文件??
  • 我收到了这些文件。就像一个文件存档器,保存所有处理过的信息:D

标签: bash unix split grep


【解决方案1】:

使用 awk :

$ cat a
field1a|field2a|field3a|field4a|field5a|field6a|field1d|field2d|field3d|field4d|field5d|field6d|field1m|field2m|field3m|field4m|field5m|field6m|field1z|field2z|field3z|field4z|field5z|field6z|



$ awk -F"|" '{for (i=1;i<NF;i=i+6) {for (j=0; j<6; j++) printf $(i+j)"|"; printf "\n"}}' a

field1a|field2a|field3a|field4a|field5a|field6a|
field1d|field2d|field3d|field4d|field5d|field6d|
field1m|field2m|field3m|field4m|field5m|field6m|
field1z|field2z|field3z|field4z|field5z|field6z|

在这里您可以轻松设置线条的长度。

希望这会有所帮助!

【讨论】:

    【解决方案2】:

    您可以使用sed 将行拆分为多行:

     sed 's/\(\([^|]*|\)\{6\}\)/\1\n/g' input.txt > output.txt
    

    解释:

    • 我们必须对(){} 使用大量的反斜杠转义,这使得代码有点不可读。

    • 但简而言之:

      • s//\1 之间的术语 (([^|]*|){6})(为了便于阅读删除了反斜杠)将匹配:

        • [^|]* 除“|”外的任何字符,重复多次

        • | 后跟一个“|”

        • 上面明显是一列,用括号()组合在一起

        • 整个组重复6次{6}

        • 这又与括号()组合在一起,形成一个完整的集合

    该术语的其余部分很容易阅读:

    • 将上面(6个字段的整个数据集)替换为\1\n//g之间的部分

    • \1指的是sed-expression中的“第一”组(开始的“第一”组,所以它是6个字段的整个数据集)

    • \n 是换行符

    • 所以自己替换整个 6 个字段的数据集,后跟换行符

    • 并反复这样做(尾随g

    【讨论】:

      【解决方案3】:

      您可以使用 sed 将每 6 个 | 转换为换行符。

      在我的 tcsh 版本中,我可以做到:

      sed 's/\(\([^|]\+|\)\{6\}\)/\1\n/g' filename
      

      考虑一下:

      > cat bla
      a1|b2|c3|d4|
      
      > sed 's/\(\([^|]\+|\)\{6\}\)/\1\n/g' bla
      a1|b2|
      c3|d4|
      

      这就是正则表达式的工作方式:

      • [^|] 是任何非| 字符。
      • [^|]\+ 是至少一个非| 字符的序列。
      • [^|]\+| 是至少一个非| 字符后跟| 的序列。
      • \([^|]\+|\) 是至少一个非| 字符后跟| 的序列,组合在一起
      • \([^|]\+|\)\{6\} 是连续 6 个这样的组。
      • \(\([^|]\+|\)\{6\}\) 是 6 个连续的此类组,组合在一起。

      替换只是采用这 6 个组的序列,并在末尾添加一个换行符。

      【讨论】:

      • sed 通常是一个独立的二进制文件,因此不依赖于您使用的 shell。
      • @umläute:你说得对,但是转义括号和| 可能有不同的要求,所以我认为最好提一下。
      【解决方案4】:

      这就是我将如何使用awk

      awk -v RS="|" '{printf $0 (NR%7?RS:"\n")}' file
      field1a|field2a|field3a|field4a|field5a|field6a|[...]
      field1d|field2d|field3d|field4d|field5d|field6d|[...]
      field1m|field2m|field3m|field4m|field5m|field6m|[...]
      field1z|field2z|field3z|field4z|field5z|field6z|
      

      只需将NR%7 调整为适合您的字段数。

      【讨论】:

      • +1 虽然最好修改ORSawk -v RS="|" '{ORS=(NR%7?"|":"\n")}1' file
      • 谢谢。将"|" 更改为RS
      • 嗯,不错!我知道可以为这个问题做一些 NR/RS 技巧:)
      【解决方案5】:

      在六个块上打印行怎么样?

      $ awk 'BEGIN{FS=OFS="|"} {for (i=1; i<=NF; i+=6) {print $(i), $(i+1), $(i+2), $(i+3), $(i+4), $(i+5)}}' file
      field1a|field2a|field3a|field4a|field5a|field6a
      field1d|field2d|field3d|field4d|field5d|field6d
      field1m|field2m|field3m|field4m|field5m|field6m
      field1z|field2z|field3z|field4z|field5z|field6z
      

      说明

      • BEGIN{FS=OFS="|"}设置输入输出字段分隔符为|
      • {for (i=1; i&lt;=NF; i+=6) {print $(i), $(i+1), $(i+2), $(i+3), $(i+4), $(i+5)}} 循环遍历 6 个块的项目。每一次,打印其中的 6 个。就像print 写了一个新行,那么你就完成了。

      【讨论】:

        【解决方案6】:

        如果您想将文件视为多行,则将\n 设为字段分隔符。例如,要获取第 2 列,只需执行以下操作:

        tr \| \\n < input-file | sed -n 2p
        

        要查看哪些列与正则表达式匹配,请执行以下操作:

        tr \| \\n < input-file | grep -n regex 
        

        【讨论】:

        • 是的,第二个就是我需要的!谢谢。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-11
        相关资源
        最近更新 更多