【问题标题】:Filter a list of paths in shell script过滤shell脚本中的路径列表
【发布时间】:2021-09-04 14:30:23
【问题描述】:

我有一个包含路径列表的文件(“dump_file”)(从hadoop fs -ls 输出生成),格式如下:

d hdfs 0 2021-06-01-13:14 /dir1
d hdfs 0 2021-06-01-13:14 /dir1/dir2
d hdfs 0 2021-06-01-13:14 /dir1/dir2/dir3
- abcdef 1201 2021-06-01-13:15 /dir1/dir2/dir3/file1
- abcdef 78441 2021-06-01-13:16 /dir1/dir2/dir3/file2
d hdfs 0 2021-06-01-13:14 /dir1/dir2/dir4
d hdfs 0 2021-06-01-13:14 /dir1/dir2/dir4/dir5
- abcdef 1201 2021-06-01-13:15 /dir1/dir2/dir4/file11
- abcdef 78441 2021-06-01-13:16 /dir1/dir2/dir4/file22
d hdfs 0 2021-06-01-13:14 /dir1/dir6/dir7

我的目标是提取任何给定节点的第一级子节点。 到目前为止,这就是我得到的(以“dir1”为例):

grep -Eio "/dir1.[^\/]+" < dump_file | sort -u | awk -F "/" '{ print $NF }'
dir2
dir6

但我也想拥有匹配行的第一个字段,如下所示:

d hdfs 0 2021-06-01-13:14 dir2
d hdfs 0 2021-06-01-13:14 dir6

“dir1/dir2”作为值应该返回:

d hdfs 0 2021-06-01-13:14 dir3
d hdfs 0 2021-06-01-13:14 dir4

"目录1/目录2/目录4:

d hdfs 0 2021-06-01-13:14 dir5
- abcdef 1201 2021-06-01-13:15 file11
- abcdef 78441 2021-06-01-13:16 file22

你知道我该怎么做吗?谢谢!

【问题讨论】:

  • 如果你提供dir1作为输入,输出不应该是dir2, dir6吗?
  • 你是对的,固定

标签: shell awk grep


【解决方案1】:
#!/usr/bin/perl -sl

$re = qr[^((?:\S+\s+){4})/\Q$dir\E/([^/]+)];
while (<>) {
  chomp;
  print $1.$2 if m[$re]o and !$seen{$2}++;
}
perl above.pl -dir=dir1/dir2 file

这是基于使用两个捕获组的 perl 正则表达式 - 一个捕获前四个字段,另一个捕获“/dir1/dir2/”之后的部分。 \Q...\E 用于转义任何正则表达式元字符。

pcregrep 可以使用相同的正则表达式(+sort -u 删除重复项):

pcregrep -o1 -o2 '^((?:\S+\s+){4})/dir1/dir2/([^/]+)' file | sort -uk5

【讨论】:

    【解决方案2】:

    使用您显示的示例,请尝试遵循awk 代码。传递您要在此awk 程序的值变量中的 Input_file 中查找的字符串值。

    awk -v value="dir1" '
    BEGIN{ len=length(value) }
    match($0,"/"value"/[^/]*"){
      matVal=substr($0,RSTART+len+2,RLENGTH-len-2)
      if(!arr[matVal]++){
        print substr($0,1,RSTART-1) matVal
      }
    }
    ' Input_file
    

    说明:为上述添加详细说明。

    awk -v value="dir1" '          ##Starting awk program from here, setting value to string which we want to look for.
    BEGIN{ len=length(value) }     ##Creating len which has length of value here in BEGIN section.
    match($0,"/"value"/[^/]*"){    ##Using match function to match given string along with next level of it here.
      matVal=substr($0,RSTART+len+2,RLENGTH-len-2) ##Creating matVal which has matched value sub string here.
      if(!arr[matVal]++){          ##Checking condition if value already does not exist in array then do following.
        print substr($0,1,RSTART-1) matVal  ##printing rest of line and matched value(only directory level) here.
      }
    }
    ' Input_file                   ##Mentioning Input_file name here.
    

    编辑: 使用 OP 传递 dir1/dir2dir1dir1/dir2/dir3 的样本,并根据 cmets 忽略传递值的路径,例如 foo/dir1/dir2处于子目录模式,然后可以尝试跟随,请注意,如果您的路径包含正则表达式元字符,这将失败(如果可以的话,我会尝试修复它)。

    awk -v value="dir1/dir2" '
    BEGIN{ len=length(value) }
    match($0,"[[:space:]]+/"value"/[^/]*"){
      matVal=substr($0,RSTART,RLENGTH)
      sub(/^[[:space:]]+/,"",matVal)
      sub("^/"value"/","",matVal)
      if(!arr[matVal]++){
        print substr($0,1,RSTART-1) OFS matVal
      }
    }
    '  Input_file
    

    【讨论】:

    • 我必须承认,我需要一些时间才能完全理解它,但它非常有效!谢谢!
    • @Joulss,欢迎您。我已经为代码添加了详细的解释,如果有任何疑问,请随时在这里发表评论,干杯
    • 实际上我还必须能够处理结果的限制和偏移(多页结果)。我最初打算用 grep 来做,但现在没有 grep...
    • @RavinderSingh13 Ed morton 关于这两个问题(正则表达式运算符和重复项)是正确的。但是您的解决方案比他的解决方案快得多(而且我必须解析的路径文件很大)。您认为有可能在保持良好性能的同时解决问题吗?
    • 感谢更新,它似乎解决了重复问题和正则表达式元字符之一(至少当目录名称包含一个点时)
    【解决方案3】:

    这可能是你想要做的:

    $ cat tst.awk
    {
        head = $0
        sub("[[:space:]]+/.*","",head)
        sub("[^/]+","")
    }
    index($0,"/" tgt "/") == 1 {
        $0 = substr($0,length(tgt) + 3)
        sub("/.*","")
        if ( !seen[$0]++ ) {
            print head, $0
        }
    }
    

    $ awk -v tgt='dir1' -f tst.awk file
    d hdfs 0 2021-06-01-13:14 dir2
    d hdfs 0 2021-06-01-13:14 dir5
    

    $ awk -v tgt='dir1/dir2' -f tst.awk file
    d hdfs 0 2021-06-01-13:14 dir3
    d hdfs 0 2021-06-01-13:14 dir4
    

    假设您只想指定头目录而不是路径作为目标的原始答案:

    $ cat tst.awk
    {
        head = $0
        sub("[[:space:]]+/.*","",head)
        sub("[^/]+","")
        nd = split($0,dirs,"/")
    }
    (nd>2) && (dirs[2] == tgt) && !seen[dirs[3]]++ {
        print head, dirs[3]
    }
    

    $ awk -v tgt='dir1' -f tst.awk file
    d hdfs 0 2021-06-01-13:14 dir2
    d hdfs 0 2021-06-01-13:14 dir5
    

    以上假设您的目标目录名称都不包含\n 之类的转义序列。

    【讨论】:

    • 您对包含正则表达式元字符的目录名称的问题是正确的。我试过你的脚本,它似乎只在第一级工作(用dir1/dir2 测试什么都不返回),它没有列出文件(也许我在问题中不清楚)
    • 对,它被设计为使用目标第一个目录名称,如dir1,如您的问题所示。如果您还有其他未提及的用例,请编辑您的问题以阐明您的要求并包括一个示例。如果您希望能够将多目录路径指定为易于处理的目标,那么它看起来不像您想要的问题。
    • dir1 是一个例子,但我提到了My goal is to extract 1st level children of a given node。也许不清楚,但我没有说给定的节点应该只是第一级。无论如何,我会编辑我的问题,谢谢。
    • 新解决方案运行良好,谢谢。大文件存在性能问题,但我会努力改进。
    • 我认为主要的问题是node这个词——它可能意味着directory或者它可能意味着path或其他东西。听到任何大小的文件都存在性能问题,我感到很震惊。
    猜你喜欢
    • 1970-01-01
    • 2010-10-12
    • 2011-07-15
    • 2013-02-09
    • 1970-01-01
    • 1970-01-01
    • 2012-08-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多