2017-01-24 316 views
2

我试图通过HDFS搜索镶木地板文件并将其列出。我使用这个,这很好。它看起来在所有的子目录中/sources.works_dbo,给我所有的实木复合地板的文件:递归搜索grep

hdfs dfs -ls -R /sources/works_dbo | grep ".*\.parquet$" 

然而,我只想返回每个子目录遇到的第一个文件,以便每个子目录只出现在输出中的一行中。说我有这样的:

sources/works_dbo/test1/file1.parquet 
sources/works_dbo/test1/file2.parquet 
sources/works_dbo/test2/file3.parquet 

当我运行我的命令,我希望输出是这样的:

sources/works_dbo/test1/file1.parquet 
sources/works_dbo/test2/file3.parquet 

回答

1

您可以使用sort -u(唯一的)与/作为分隔符,使用前三个领域作为关键。 -s选项(“稳定”)确保保留的文件是每个子目录遇到的第一个文件。

对于此输入

sources/works_dbo/test1/file1.parquet 
sources/works_dbo/test1/file2.parquet 
sources/works_dbo/test2/file3.parquet 

结果是

$ sort -s -t '/' -k 1,3 -u infile 
sources/works_dbo/test1/file1.parquet 
sources/works_dbo/test2/file3.parquet 
+0

这实在是优雅。 – codeforester

1

如果子目录是可变长度的,这awk溶液可以派上用场:

hdfs dfs -ls -R /sources/works_dbo | awk ' 
    BEGIN{FS="/"; OFS="/";} 
    {file=$NF;     // file name is always the last field 
     $NF=""; folder=$0;   // chomp off the last field to cache folder 
     if (!(folder in seen_dirs)) // cache the first file per folder 
      seen_dirs[folder]=file; 
    } 
    END{ 
     for (f in seen_dirs)  // after we've processed all rows, print our cache 
      print f,seen_dirs[f]; 
    }' 
2
... | awk '!seen[gensub(/[^/]+$/,"",1)]++' file 
sources/works_dbo/test1/file1.parquet 
sources/works_dbo/test2/file3.parquet 

Ť上面他用GNU AWK的gensub(),与其他awks你使用一个变量和子():

awk '{path=$0; sub(/[^/]+$/,"",path)} !seen[path]++' 

它将为路径任何长度的任何混合物工作。

1

使用Perl:

hdfs dfs -ls -R /sources/works_dbo | grep '.*\.parquet$' | \ 
    perl -MFile::Basename -nle 'print unless $h{ dirname($_) }++' 

perl的上述命令:

  • -M负载File::Basename模块;
  • -n会导致Perl对每个输入行应用通过-e传递的表达式;
  • -l保留行终止符;
  • $_是保留当前读取行的默认变量;
  • dirname($_)返回由$_指定的路径的目录部分;
  • $h是一个散列,其中键是目录名,值是整数0,1,2等;
  • 该行被打印到标准输出,除非在前面的迭代中看到目录名称,即散列值$h{ dirname($_) }不为零。

顺便说一句,而不是通过grep管道hdfs dfs -ls -R的结果,你可以使用find命令:

hdfs dfs -find /sources/works_dbo -name '*.parquet'