如何让perl正确传递具有多个参数和复杂文件路径(空格和符号)的命令行参数?

时间:2016-07-06 09:00:12

标签: perl cmd

我有一个小的perl脚本,它从excel文件中收集文件路径,并通过命令行传递给perltex,然后perltex根据所选的文件和路径编译pdf。

我的问题是,当我引入更复杂的文件路径(这是基于最终用户池的网络设置所必需的)时,perltex无法找到文件路径,无法在空间中剪切它们。

MWE是以下

#!/usr/bin/perl

use strict;
use warnings;
use 5.14.2;

use Text::Template;
use Spreadsheet::Read;
use Spreadsheet::ParseXLSX;
use utf8;
use charnames qw( :full :short );
use autodie;

my $row = 5;
my $col = 15;
my $File = "C:/Users/me/Desktop/Reporting-Static/Input-test1.xlsm";
my $parser   = Spreadsheet::ParseXLSX->new();             
my $workbook = $parser->parse($File);
my $worksheet = $workbook->worksheet("Input");
my $cell = $worksheet->get_cell($row, $col);
my $Filename = $cell->Value();

my $texfile = "C:/Users/me/Desktop/Reporting-Static/file.tex"; 
# can't find this file if there are spaces in the address
system("perltex", "--latex=pdflatex", "--nosafe", "--jobname=$Filename", "$texfile");

if ( $? == -1 )
 {
   print "command failed: $!\n";
 }
 else
 {
   printf "command exited with value %d", $? >> 8;
 }

 exit;

然而,我将文件夹名称更改为带有空格的那一刻,例如。 "报告静态"它无法找到tex文件。

我已经在堆栈交换和其他网站上阅读了其他几篇关于此的帖子,但无论出于何种原因,建议的解决方案似乎对我不起作用。我试过了

my $texfile = "C:/Users/me/Desktop/Reporting Static/file.tex";
my $texfile = C:/Users/me/Desktop/"Reporting Static"/file.tex;
my $texfile = "\"C:/Users/me/Desktop/"Reporting Static"/file.tex\"";
my $texfile = "\"C:/Users/me/Desktop/Reporting Static/file.tex\"";
my $texfile = "C:/Users/me/Desktop/Reporting^ Static/file.tex";
my $texfile = "C:/Users/me/Desktop/Reporting\^ Static/file.tex";

除了以上的一些其他组合或变化之外,都没有成功。我也尝试用单引号替换双引号,以便perl不会内插内容。

我还尝试在命令提示符中手动输入上述所有内容,以检查perl是否将命令传递给命令行,但仍然没有运气。

我知道我可以使用' dir / X~1 c:\'命令查找避免空格的系统名称分配,但想法是文件名和位置将是动态的,并作为部门和网站的功能进行更改,所以我宁愿避免尝试编写一个脚本来查找此路径名,用它来替换所有使用空格或其他特殊字符的位置。

我最后的想法是,这个问题可能与perltex传递它的论据有关,但我无法找到任何文件(我可以遵循......)关于如何文件功能的这个特殊方面。

所以我的问题是,在我读到的关于如何正确地将这些路径传递给perltex的其他答案中,我是否缺少一些我没有注意到的内容,是否可能存在某种不兼容的问题。关于这一点,这是与perltex相关的更多概率而不是perl或cmd,还是有一些完全不同的东西,我不知道这是阻止它工作???

编辑: 从cmd提示符perltex返回"无法找到路径X,请输入另一个文件位置"。到目前为止,我还没有通过输入C:/ Users / me / Desktop /"报告静态" /file.tex'来测试重新输入路径。 (开头没有引号)随后被接受并运行。但最初传递它这条路径不起作用,这表明一些内部perltex代码接受初始路径不同,以便在错误后重新分配相同的路径....不太清楚该怎么做。

编辑: 我提取的@latexcmdline的内容

    $VAR1 = [
      'pdflatex',
      '--jobname=--',
      '\\makeatletter\\def\\plmac@tag{AYNNNUVKQVJGZKKPGPTH}\\def\\plmac@tofile{Perl.topl}\\def\\plmac@fromfile{Perl.frpl}\\def\\plmac@toflag{Perl.tfpl}\\def\\plmac@fromflag{Perl.ffpl}\\def\\plmac@doneflag{Perl.dfpl}\\def\\plmac@pipe{Perl.pipe}\\makeatother\\input C:/Users/me/Desktop/PERLTEST/Perl',
      'Modules/RevuedeProjetDB.tex'
    ];

这是通过插入

来完成的
    use File::Slurp;
use Data::Dumper;
write_file 'C:\Users\me\Desktop\PERLTEST\mydump.log', Dumper( \@latexcmdline );
在exec命令之前

2 个答案:

答案 0 :(得分:1)

更新

我最初建议您使用String::ShellQuote,但该模块仅适用于Linux,因此当我意识到您的问题是关于Windows系统时,我删除了我的答案

似乎还有Win32::ShellQuote对Windows做同样的事情,所以我正在更新我的建议

正如我之前所说,问题是perltex本身并不能正确处理包含空格的路径,即使它们被正确地作为@ARGV的单个元素传递。我相信解决方案是通过路径包括封闭引号,虽然我从来没有能够正确测试,因为我没有LaTex安装

不幸的是,即使我通过qq{"$texfile"},引号在到达目标程序之前仍然被剥离,所以它们必须以某种方式受到保护

您需要该模块中的quote_system函数,该函数将准备一个字符串列表,以便它们保留任何引号

使用quote_system(qq{"$texfile"})参数可以在我的测试中生成正确的结果。这相当于传递qq{"\\"$texfile\\""}但不那么丑陋

所以你的系统调用应该是这样的(对perltex.pl进行 no 修改)

我已将相同的原则应用于$Filename,因为它也可能包含空格

use Win32::ShellQuote 'quote_system';

system(quote_system(
    'perltex',
    '--latex=pdflatex',
    '--nosafe',
    qq{--jobname="$Filename"},
    qq{"$texfile"},
));


好吧,我有各种各样的解决方案

我怀疑这个问题是,虽然路径作为单个字符串传递给perltex.pl,但后者在收到路径时没有正确处理路径

临时解决方法是破解perltex.pl

我的perltex.pl版本的第82行(源代码中没有版本号)读取

$latexcmdline[$firstcmd] = "\\input $option";

如果您将其更改为

$latexcmdline[$firstcmd] = qq{\\input "$option"};
然后一切都应该好。但是,只有当perltex的作者分发时,这才是一个可靠的解决方案。与此同时,我正在寻找来自主叫方的更好的解决方案

答案 1 :(得分:0)

解决此问题有两个步骤。

  1. 弄清楚如何将正确的参数传入外部 程序
  2. 了解如何从Perl程序中执行此操作。
  3. 对于第1步,我发现这样的程序很有用。

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    print "Received ", scalar @ARGV, " arguments:\n";
    for (1 .. @ARGV) {
      print "$_: $ARGV[$_ - 1]\n";
    }
    

    它只是解释了它在命令行上收到的参数。您可以使用它代替“perltex”进行测试。

    你会看到,如果你给它一个包含空格的参数,那么它被解释为被调用的程序作为多个参数。绕过的方法是引用包含空格的参数。我似乎记得Windows坚持使用双引号(出于我永远无法记住的原因)。

    所以我认为你想要这个:

    system('perltex', '--latex=pdflatex', '--nosafe', "--jobname=\"$Filename\"", "\"$texfile\"");
    

    我已经双引两个文件名。当然,那些逃脱的双引号字符看起来真的很难看,Perl让我们qq(...)让它看起来更漂亮。

    system('perltex', '--latex=pdflatex', '--nosafe', qq(--jobname="$Filename"), qq("$texfile"));
    

    如果那不太正确,那么我之前展示的程序将更容易找到解决方案。

    更新:鲍罗丁的评论如下,对$texfile没有任何影响是准确的。我们将列表传递给system()的事实意味着shell根本不涉及。