【问题标题】:Split file into multiple when special char met遇到特殊字符时将文件拆分为多个
【发布时间】:2017-09-27 08:47:15
【问题描述】:

我有一个主文件如下:

/* ------------- AAAAAAAA ------------- */
some
lines 
here
/* ------------- BBBBBBBB ------------- */
more
things
/* ------------- CCCCCCCC ------------- */
there
a 
few
more
lines

我的最终目标是创建一个仅包含包含特定字符串的块的文件,例如,如果该字符串是 lines,那么我将有一个像这样的输出文件:

/* ------------- AAAAAAAA ------------- */
some
lines 
here
/* ------------- CCCCCCCC ------------- */
there
a 
few
more 
lines

为了达到我的目标,我首先尝试通过 bock 将我的主文件拆分为子文件以获得类似

  • 文件-1
  • 文件-2
  • 文件-3

然后我计划检查每个文件,如果随后包含搜索到的字符串,那么我将它们附加回我的新主文件。

老实说,我不知道这是否是最好的方法,而且我的主文件中有 30139 行的 1600 多个块,所以要解析的内容很多。

但是,如果我保持这种方式来完成这项工作,我的代码仍然存在问题:

#!/bin/ksh
i=0
while IFS=\| read -r "line"; do
        if [ `echo $line | grep '/* ------' | wc -l` -eq 1 ]; then
                i=$((i+1))
        fi
        echo $line > "file-$i"
done < $1

由于每个块由/* -------- 分隔,如果我执行echo $line,输出将是我的根目录(/etc/tmp 等)而不是$line 本身。

所以我知道这是一个 2 个问题的帖子,但因为第二个问题可以使用不同的脚本方式绕过,所以它肯定是链接的。

编辑:

解决方案必须在 korn shell 中,因为我无法在这台机器上安装任何东西

【问题讨论】:

  • 请在您的问题中添加:解决方案必须在 korn shell 中

标签: linux shell ksh


【解决方案1】:

awk 中的另一个:

$ awk '
function dump() {         # define a function to avoid duplicate code in END
    if(b~/lines/)         # if buffer has "lines" in it
        print b           # output and ...
    b="" }                # reset buffer
/^\/\*/ { dump() }        # at the start of a new block dump existing buffer
{ b=b (b==""?"":ORS) $0 } # gather buffer
END{ dump() }             # dump the last buffer also
' file
/* ------------- AAAAAAAA ------------- */
some
lines 
here
/* ------------- CCCCCCCC ------------- */
there
a 
few
more
lines

【讨论】:

  • 解决方案必须在 korn shell 中
  • 好的,很抱歉。应该说明的是,在 OP 中,它被标记为 shell 和 linux 以及 ksh。
【解决方案2】:

如果您不介意使用 Perl,那么有一个很好的 one-liner 可以让您轻松实现。

你唯一需要的就是添加这样一行:

/* ------------- END ------------- */

在文件的最后。所以变成这样:

/* ------------- AAAAAAAA ------------- */
some
lines 
here
/* ------------- BBBBBBBB ------------- */
more
things
/* ------------- CCCCCCCC ------------- */
there
a 
few
more
lines
/* ------------- END ------------- */

现在借助 模式:

\/\*.*?(?=\/\*)

您可以单独匹配每个部分。例如这部分:

/* ------------- AAAAAAAA ------------- */
some
lines 
here

因此,如果您将结果存储在最后的 array 中,您将拥有一个包含 3 部分的数组。最终您可以在每个部分申请lines。如果找到,则打印该部分。

单行

perl -ne 'BEGIN{$/=undef;}push(@arr,$&) while/\/\*.*?(?=\/\*)/smg;END{for (@arr){print if /lines/g }}' file

输出将是:

/* ------------- AAAAAAAA ------------- */
some
lines 
here
/* ------------- CCCCCCCC ------------- */
there
a 
few
more
lines

如果你申请more

/* ------------- BBBBBBBB ------------- */
more
things
/* ------------- CCCCCCCC ------------- */
there
a 
few
more
lines

基于@batMan 解决方案

命令行解决方案:

tr '\n' ';' < file | grep -Po '\/\*.*?(?=\/\*)' | grep lines | tr ';' '\n'

它的输出:

/* ------------- AAAAAAAA ------------- */
some
lines 
here

/* ------------- CCCCCCCC ------------- */
there
a 
few
more
lines

【讨论】:

  • 这是一个很好的解决方案,但不幸的是我不能使用 Perl;它必须在 korn shell 中:/
  • 好的。按照我已经走了的技术。首先阅读整个文件。然后通过应用适当的模式将其分成三个部分。最后你有 3 个独立的部分。然后您可以应用您的字符串:lines 或其他任何内容。
【解决方案3】:

当你真的想使用while read 构造时,尽量避免额外的文件和系统调用。

matched=0
all=
while IFS= read -r line; do
  if [[ ${line} =~ "/* ----"* ]]; then
      if [ ${matched} -eq 1 ]; then
         printf "%s\n" "${all}"
      fi
      all=
      matched=0
  fi
  all="${all}${line}
"
  if [[ "${line}" =~ line ]]; then
    matched=1
  fi
done < <(cat mainfile; echo "/* ---- The End --- */" )

【讨论】:

    【解决方案4】:

    使用 awk

    awk -v RS="/[*]" '/lines/{printf "/*"$0}' file
    

    输出:

    /* ------------- AAAAAAAA ------------- */
    some
    lines
    here
    /* ------------- CCCCCCCC ------------- */
    there
    a
    few
    more
    lines
    

    【讨论】:

    • 哦,根据您的tr,我创建了一个命令行解决方案。谢谢你:)
    • 欢迎!顺便说一句,我删除了 tr 并仅使用 awk 更新了解决方案,因为使用 tr 需要调用多个管道并降低性能。避免这种情况。检查更新的:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多