列出在文本文件中找不到的字符串

时间:2015-02-04 13:05:32

标签: perl shell command-line awk sed

我有一个包含数百个文件的目录。目录中所有文件的名称也列在Javascript文件中(参见下文)。我想在文本文件中找到目录中不存在的文件名。例如:

% ls ./images/ 
a.png
c.png
x.png

文件:

{
   name: "A",
   filename: "a.png"

},
{
   name: "X",
   filename: "x.png"

}

在这种情况下,输出应为“c.png”。

我找到了一些能找到字符串的awk脚本(参见:awk script: check if all words(fields) from one file are contained in another file)。但是在我的情况下,我想找到匹配的文件列表。

6 个答案:

答案 0 :(得分:2)

您可以通过以下命令

完成您想要的任务
$ mawk '/filename:/{gsub("\"","",$2);names[$2]}
        END{while(("ls ?.png"|getline fnm)>0){
               if(!(fnm in names)) print fnm
        }}' file.dat

在第一行中,我们扫描数据文件,查找字符串"filename",从引号中删除文件名,最后将文件名保存在数组中。

END我们对相关ls命令的输出进行循环,如果当前文件名未保存在数组中,我们将其打印到stdout。

最困难的部分是获得最终for循环的正确语法......


附录

继续原始海报的评论,这里是一个 修改后的脚本版本

$ mawk '/filename:/{gsub("\"","",$2);names[$2]}
        END{while(("ls /var/www/html/img/*.png"|getline path)>0){
                n = split(path, parts, "/")
                fnm = parts[n]
                if(!(fnm in names)) print fnm
        }}' file.dat

适用于固定目录名称。如果目录名必须是 在运行时给出,请尝试以下

 $ extra_png () {
 mawk '/filename:/{gsub("\"","",$2);names[$2]}
        END{while(("ls '"$2"'/*.png"|getline path)>0){
                n = split(path, parts, "/")
                fnm = parts[n]
                if(!(fnm in names)) print fnm
        }}' "$1"
 }
 $ extra_png data.txt /var/www/html/img
 c.png
 $

其中第一个命令定义了一个接受as的shell函数 参数数据文件和要扫描的目录。

作为旁注,这个awk脚本找到了未提及的png文件 数据文件(根据OP请求),知道可能很有趣 如果文件中提到的文件名不存在于 目录。但这可能是另一个问题的主题。

答案 1 :(得分:2)

$ cat tst.awk
BEGIN {
    while (ARGC > 2) {
        sub(/.*\//,"",ARGV[--ARGC])
        targets[ARGV[ARGC]]
        delete ARGV[ARGC]
    }
}
sub(/.*filename:[[:space:]]*"/,"") && sub(/\"[[:space:]]*$/,"") {
    present[$0]
}
END {
    print "Present:"
    for (file in present) {
        if (file in targets) {
            print "\t" file
        }
    }

    print "\nAbsent:"
    for (file in targets) {
        if (! (file in present) ) {
            print "\t" file
        }
    }
}

$ awk -f tst.awk file image/*
Present:
        x.png
        a.png

Absent:
        c.png

请注意,无论您的文件名包含哪些字符(包括空格和双引号),这都会有效,并且不会尝试解析ls的输出,这总是一个坏主意。

答案 2 :(得分:2)

如果您可以从CPAN安装一些很酷的模块,我会为您的任务提出一个更清洁(IMHO)的脚本:

#!/usr/bin/perl

use strict; use warnings; use 5.010; 
use JSON;
use Path::Tiny;

my $json_data = path('images.json')->slurp;
my $data = decode_json( $json_data );

my %files_to_check = map { $_->basename => 0 } path('images')->children; 
my @files_in_json = map { $_->{filename} } @$data; 
delete @files_to_check{ @files_in_json }; # delete all files we have in JSON

say "$_" for sort keys %files_to_check;

答案 3 :(得分:2)

每当您认为必须在列表中找到或未找到某些内容时,请考虑哈希。哈希是一种索引列表的快捷方式,因为只需查看密钥就可以找到列表中是否存在某些内容。

在本程序的前半部分,我将浏览您的JSON文件,查找文件名并将其存储在名为%files的哈希中。在下半部分,我将浏览我的png文件所在的目录,并检查每个文件是否在%files哈希中。如果某个特定条目不存在,我知道它不在我的JSON文件中。

  

注意:我本可以使用use JSON;来解析我的JSON文件。但是,在本次演示中,我只是在寻找filename行以保持简单。如果这是一个实际的程序,请使用JSON模块。

#! /usr/bin/env perl
use strict;
use warnings;
use autodie;
use feature qw(say);

use constant {
    FILE_NAME       => 'file.txt',
    DIR_NAME        => 'temp',
};

#
# Build the %files hash
#
open my $fh, "<", FILE_NAME;
my %files;
while ( my $line = <$fh> ) { 
    chomp $line;
    next unless $line =~ /\s+filename:\s+"(.+)"/;
    my $file = $1;
    $files{$file} = 1;
}
close $fh;

#
# Go through directory looking for entries not in %files
#
opendir my $dh, DIR_NAME;
while ( my $file = readdir $dh ) {
    next if $file eq "." or $file eq "..";
    if ( not exists $files{$file} ) {
        say qq(File "$file" not in list);
    }
}
closedir $dh;

答案 4 :(得分:1)

列出数据文件中但使用Perl从目录列表中丢失的文件的一种简单方法是使用目录中的文件测试(或传递完整路径)文件的名称&#34;如果文件不存在&#34;或&#34;除非文件存在&#34;:

perl -nE 'map { say if !-e $_ } m/\"(.*)\"/ if /filename/' data.js

执行相反的操作(您的示例) - 从目录列表中打印文件名($fname)是否可以&#39;可以在从文件列表数据(@m)创建的名称数组(data.js)中找到:

perl -nE 'push @m, m/\"(.*)\"/ if /filename/ }{ 
         for $fname (glob "*"){ say $fname if !grep { $_ eq $fname } @m}' data.js

以下是@neuhaus发布的完整脚本变体。区别在于以下方法使用IO::All来创建IO&#34;对象&#34;从目录'./images/'作为哈希,然后使用keys列出文件的名称。我修改了文本文件中的数据以说明grep unless语句:

# files.pl
use IO::All;
@files =  keys %{ io('./images/') }  ;

while(<DATA>) {
  push @flist, m/\"(.*)\"/ if /filename/  ; 
}

for $fname ( @flist) {print $fname unless grep { $_ eq $name } @files}  ;

__DATA__

{
   name: "A",
   filename: "a.png"
},
   {
   name: "X",
   filename: "x.png"
},
  {
   name: "Z",
   filename: "z.png"
}

输出(如果在包含perl files.pl目录的目录中运行./images/):

  % ls ./images/ 
  a.png x.png y.png z.png
  % perl files.pl
  y.png

__DATA__部分(代表data.js文件)中,文件名被提取到@files。目录列表中的文件打印unless可以通过grep中的@files找到它们。

这是一个版本作为一个班轮,您的数据在data.js

perl -MIO::All -lne 'push @flist, m/\"(.*)\"/ if /filename/ ; 
   }{ for $name (keys %{ io "./images/" }){ print $name 
   unless grep { $_ eq $name } @flist }' data.js

更多Unix-ish方法可能会在glob目录中使用/images/(警告:有时会出现与某些平台上带空格的文件名有关的问题):

 perl -MIO::All -lne  'push @flist, m/\"(.*)\"/ if /filename/ ; 
    }{ for $name ( glob("*.png") ){ print $name 
    unless grep { $_ eq $name } @flist }' data.js

或带有openopendir

的文件和目录句柄
... 
opendir(my $dir, ".") || die; 
@files = readdir $dir ;
...

答案 5 :(得分:-1)

以下是perl的解决方案:

@list是包含文件名的数组。

open(my $fh, "<", "input.txt");
my $contents = do { local $/ = <$fh> };
my $string = <$fh>;
close($fh);

foreach my $entry (@list) {
    print "$entry is not in file\n" if index($contents, $entry) == -1;
}