Windows cmd.exe 命令传递 Perl 系统函数在某些情况下需要双引号吗?

Posted

技术标签:

【中文标题】Windows cmd.exe 命令传递 Perl 系统函数在某些情况下需要双引号吗?【英文标题】:Windows cmd.exe command passed Perl system function needs double quotes in some cases? 【发布时间】:2021-06-29 01:29:23 【问题描述】:

Windows cmd.exe 命令 'type \Test\hello.txt' 必须用双引号引起来,以便 Perl system() 函数正确执行它。我没有在 https://perldoc.perl.org/functions/system 或 https://perldoc.perl.org/perlport#system 找到此文档 - Windows 上的双引号要求是否记录在任何地方?

以下示例代码演示了在 Windows 10 Pro 服务器版本 2004(OS Build 19041.867)的 Windows 命令提示符窗口中使用命令 perl \my\perl\perlsystest 运行时出现的问题,Perl 级别为 v5.26.3


$cmd='type \Test\hello.txt' ;   # Set Windows DOS command to be executed
system('ECHO '.$cmd) ;          # Display command to be executed
system($cmd) == 0               # Execute command - note "\Test\hello.txt not found" error but with $? == 0
  or die "system(\"$cmd\") failed: $?!" ;
if ($? == -1) print "system(\"$cmd\") failed to execute: $!!\n"
elsif ($? & 127) printf "system(\"$cmd\") child died with signal %d, %s coredump\n",
  ($? & 127),  ($? & 128) ? 'with' : 'without' ; exit
elsif ($? != 0) printf "system(\"$cmd\") child exited with value %d\n", $? ; exit

$cmd='"'.$cmd.'"' ;             # Surround command string in double quotes
system('ECHO '.$cmd) ;          # Display command to be executed
system($cmd) == 0               # Execute command - note that command succeeds
  or die "system(\"$cmd\") failed: $?!" ;
if ($? == -1) print "system(\"$cmd\") failed to execute: $!!\n"
elsif ($? & 127) printf "system(\"$cmd\") child died with signal %d, %s coredump\n",
  ($? & 127),  ($? & 128) ? 'with' : 'without' ; exit
elsif ($? != 0) printf "system(\"$cmd\") child exited with value %d\n", $? ; exit

$cmd='type \Test\10hello.txt' ; # Set Windows DOS command to be executed
system('ECHO '.$cmd) ;          # Display command to be executed -  note that "type \Teshello.txt" is echoed
system($cmd) == 0               # Execute command - note "\Test\hello.txt not found" error but with $? == 0
  or die "system(\"$cmd\") failed: $?!" ;
if ($? == -1) print "system(\"$cmd\") failed to execute: $!!\n"
elsif ($? & 127) printf "system(\"$cmd\") child died with signal %d, %s coredump\n",
  ($? & 127),  ($? & 128) ? 'with' : 'without' ; exit
elsif ($? != 0) printf "system(\"$cmd\") child exited with value %d\n", $? ; exit

system('"ECHO '.$cmd).'"' ;     # Display command to be executed (with ECHO command in double quotes)
$cmd='"'.$cmd.'"' ;             # Surround command string in double quotes
system($cmd) == 0               # Execute command
  or die "system(\"$cmd\") failed: $?!" ;
if ($? == -1) print "system(\"$cmd\") failed to execute: $!!\n"
elsif ($? & 127) printf "system(\"$cmd\") child died with signal %d, %s coredump\n",
  ($? & 127),  ($? & 128) ? 'with' : 'without' ; exit
elsif ($? != 0) printf "system(\"$cmd\") child exited with value %d\n", $? ; exit

【问题讨论】:

我无法在 Windows 10 Pro 上使用我的 Perl 5.30 重现(脚本正常执行,没有任何错误)。 Win32::ShellQuote 很有用。 @Mofi 和 @ikegami,感谢您的 cmets。合成这些 cmets:当处理 system() 调用时,Perl 向 Windows 发送一个命令,该命令通过将传递给 system() 调用的参数附加到 sh 变量(来自 Perl Config.pm)中的值来构建。在我的 Windows 10 上,perl -V:sh 显示 sh='cmd /x /c',因此当使用 $cmd='type \Test\hello.txt' 调用 system($cmd) 时,声称 Perl 将导致 Windows 运行命令 cmd /x /c type \Test\hello.txt 但这并不完全正确,因为我的 Windows 10 上的 cmd /x /c type \Test\hello.txt 正确键入了 \Test\hello.txt 文件,但是当使用 $cmd='type \Test\hello.txt' 调用 system($cmd) 时,system($cmd) 调用会引发错误消息\Test\hello.txt not found 但返回 $?=0 感谢@Dada,他及时评论说:“我无法在 Windows 10 Pro 上使用我的 Perl 5.30 进行复制(脚本正常执行而没有任何错误)”。 【参考方案1】:

你给system 的任何东西都必须适合处理命令的东西。原问题的 cmets 应该变成答案。

system 文档指向exec,它还说:

当参数通过系统 shell 执行时,结果受制于其怪癖和功能。有关详细信息,请参阅 perlop 中的“STRING”。

这也指向perlop 中用于反引号的条目:

如何评估该字符串完全取决于系统上的命令解释器。在大多数平台上,如果您希望按字面意思对待 shell 元字符,则必须保护它们。这在实践中很难做到,因为不清楚如何转义哪些字符。

Perl 在这里没有解决单个系统,因为它支持的系统太多了(即使使用这些系统的每个人都已经消失了)。


你在那里贴了一堵文字墙,我不得不重写它来看看你在做什么。与其重复相同的代码,一个子程序(有一些合理的格式)对读者很礼貌:)

my @commands = (
    'type \Test\hello.txt',
    q("type \Test\hello.txt")
    'type \Test\10hello.txt',
    );

foreach my $command ( @commands ) 
    try_it( $command );
    

sub try_it  
    my( $cmd ) = @_;
    system('ECHO '.$cmd); 
    system($cmd) == 0               # Execute command
      or die qq(system("$cmd") failed: $?!) ;
    if ($? == -1) 
        print qq(system("$cmd") failed to execute: $!!\n);
        
    elsif ($? & 127) 
        printf qq(system("$cmd") child died with signal %d, %s coredump\n),
            ($? & 127),  ($? & 128) ? 'with' : 'without' ; 
        
    elsif ($? != 0) 
        printf qq(system("$cmd") child exited with value %d\n), $? ; 
        
    

【讨论】:

以上是关于Windows cmd.exe 命令传递 Perl 系统函数在某些情况下需要双引号吗?的主要内容,如果未能解决你的问题,请参考以下文章

通过Qt GUI向CMD传递/发出命令

Windows [cmd.exe] 命令显示超时消息框?

有啥软件可以取代windows下的cmd.exe 吗?

Windows的idea的Terminal替换为git bash(默认是Windows的cmd.exe命令行)

Windows的idea的Terminal替换为git bash(默认是Windows的cmd.exe命令行)

Wix:我无法将参数从CAQuietExec传递到cmd.exe批处理