【问题标题】:Escape whitespace when using backticks使用反引号时转义空格
【发布时间】:2010-09-25 18:59:38
【问题描述】:

我进行了搜索,从我的角度来看,使用反引号是我可以解决此问题的唯一方法。我正在尝试从 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);

【问题讨论】:

  • 换行有特殊原因吗?你不需要模拟一个虚拟的 ,假装你是在终端输入它。

标签: perl macos find whitespace backticks


【解决方案1】:

如果您只是想要操作系统上次访问时间方面的“上次访问时间”,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 "$!";

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);

【讨论】:

    【解决方案2】:

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

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

    不幸的是,这不适用于带有换行符的文件名,因为 shell 通过删除 both 字符而不只是反斜杠来处理后跟换行符的反斜杠。所以为了安全起见,请使用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】:

    如果您只是想查找上次访问时间,是否有一些奇怪的 Mac 原因您不使用 stat?什么时候会比kMDItemLastUsedDate更糟?

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

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

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

    【讨论】:

    • mdls 中的信息远不止访问时间。
    • stat 中还有更多信息。但是,他说他正在寻找最后一次访问时间。
    • 如果他的意思是操作系统意义上的“最后访问时间”,他应该使用stat 如果他的意思是文件类型的注册创建者(Mac概念)的“最后使用时间”,那么我认为@ 987654333@ 是正确的工具。我不知道他想做什么......
    • 这就是我问这个问题的原因。一切尽在我的回答中。
    【解决方案4】:

    您可以通过将命令表示为列表并结合来自IPC::System::Simplecapture() 来绕过shell:

    use IPC::System::Simple qw(capture);
    
    my $output = capture('mdls', $curr_file_path);
    

    【讨论】:

      【解决方案5】:

      在反引号内引用变量名:

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

      【讨论】:

      • 您必须比这更加小心。 " 和 ' 是合法的文件名字符。
      • 这在像sam's "holiday" song.flac 这样的文件名上失败,这是 Unix 下的合法文件名......在任何一种情况下,周围的引号都会被嵌入的单引号或双引号破坏。您需要有'sam\'s "holiday" song.flac'"sam\'s \"holiday\" song.flac" 等...
      • @drewk:请注意这种引用方法:不同的 shell 处理转义引号的行为不同:Including identical quotes within quotesecho 'Don\'t quote me' 在 cshell 和 bash 中都不起作用:引号打开和关闭替换。它们不用于指示字符串的开始和结束。因此,您只能对未引用的引号进行转义(至少从我在 bash 中的经验来看):echo 'the Sam'\'s' songs' >The Sam 的歌曲。
      • 是的,逃跑非常困难。这就是为什么我通常不这样做,而是使用不需要它的 Perl 打开。
      猜你喜欢
      • 2015-06-28
      • 2011-07-24
      • 1970-01-01
      • 2021-02-26
      • 1970-01-01
      • 1970-01-01
      • 2012-03-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多