使用反引号时逃避空格

时间:2010-09-25 18:59:38

标签: perl macos find whitespace backticks

我已经进行了搜索,从我的观点来看,使用反引号是解决这个问题的唯一方法。我正在尝试从Perl中为目录中的每个文件调用mdls命令,以查找它的上次访问时间。我遇到的问题是,我在find的文件名中有非转义空格,bash显然不喜欢。在将文件名传递给mdls之前,是否有一种简单的方法可以逃避文件名中的所有空格。如果这是一个显而易见的问题,请原谅我。我对Perl很新。

my $top_dir = '/Volumes/hydrogen/FLAC';

sub wanted { # Learn about sub routines 
    if ($File::Find::name) { 
         my $curr_file_path = $File::Find::name. "\n";
        `mdls $curr_file_path`;
         print $_;
    }
}

find(\&wanted, $top_dir);

5 个答案:

答案 0 :(得分:5)

如果您想要根据操作系统上次访问时间的“上次访问时间”,mdls是错误的工具。使用perl的stat。如果您想要根据Mac注册的应用程序(即Quicktime或iTunes的歌曲)的最后访问时间,那么mdls可能是正确的工具。 (您也可以使用osascript直接查询Mac应用...)

反引号用于捕获文本返回。由于您使用的是mdls,我认为捕获和解析文本仍然存在。

所以有几种方法:

  1. 使用system的列表表单,并且不需要引用(如果您 不关心返回文本);

  2. 在发送给sh之前使用String::ShellQuote转义文件名;

  3. 构建字符串并在发送到shell之前用单引号括起来。这比听起来更难,因为带单引号的文件名称会使你的报价失败!例如,sam's song.mp4是合法的文件名,但如果您使用单引号括起来,则会得到'sam's song.mp4',这不是您的意思......

  4. 使用open打开子进程输出的管道,如下所示:open my $fh, '-|', "mdls", "$curr_file" or die "$!";

  5. String :: ShellQuote:

    的示例
    use strict; use warnings;
    use String::ShellQuote;
    use File::Find;
    
    my $top_dir = '/Users/andrew/music/iTunes/iTunes Music/Music';
    
    sub wanted { 
        if ($File::Find::name) { 
             my $curr_file = "$File::Find::name";
             my $rtr;
             return if -d;
             my $exec="mdls ".shell_quote($curr_file);
             $rtr=`$exec`;  
             print "$rtr\n\n";
        }
    }
    
    find(\&wanted, $top_dir);
    

    管道示例:

    use strict; use warnings;
    use String::ShellQuote;
    use File::Find;
    
    my $top_dir = '/Users/andrew/music/iTunes/iTunes Music/Music';
    
    sub wanted { 
        if ($File::Find::name) { 
             my $curr_file = "$File::Find::name";
             my $rtr;
             return if -d;
             open my $fh, '-|', "mdls", "$curr_file" or die "$!";
             { local $/; $rtr=<$fh>; }  
             close $fh or die "$!";
             print "$rtr\n\n";
        }
    }
    
    find(\&wanted, $top_dir);
    

答案 1 :(得分:2)

如果您只是想找到最后一次访问时间,是否有一些奇怪的Mac因为您没有使用stat?什么时候会比kMDItemLastUsedDate更糟?

 my $last_access = ( stat($file) )[8];

似乎kMDItemLastUsedDate并不总是更新到上次访问时间。如果您通过终端处理文件(例如catmore),则kMDItemLastUsedDate不会更改,但stat返回的值是正确的。 touch似乎在两种情况下都做对了。

看起来您需要stat才能获得真正的答案,但mdls如果您正在寻找通过应用程序的访问权限。

答案 2 :(得分:2)

如果您确定文件名不包含换行符(CR或LF),那么几乎所有Unix shell都接受反斜杠引用,并且Perl具有quotemeta函数来应用它。

my $curr_file_path = quotemeta($File::Find::name);
my $time = `mdls $curr_file_path`;

不幸的是,这对于带有换行符的文件名不起作用,因为shell通过删除两个字符而不仅仅是反斜杠来处理反斜杠后跟换行符。为了确保安全,请使用String::ShellQuote

use String::ShellQuote;
...
my $curr_file_path = shell_quote($File::Find::name);
my $time = `mdls $curr_file_path`;

这应该适用于包含除NUL字符之外的任何文件名,你不应该在文件名中使用它。

这两种解决方案仅适用于Unix风格的shell。如果你在Windows上,适当的shell引用要复杂得多。

答案 3 :(得分:2)

您可以通过将命令表示为列表并与IPC::System::Simple中的capture()结合来绕过shell:

use IPC::System::Simple qw(capture);

my $output = capture('mdls', $curr_file_path);

答案 4 :(得分:1)

引用反引号中的变量名称:

`mdls "$curr_file_path"`;
`mdls '$curr_file_path'`;