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



我有一个小的perl脚本,它从excel文件中收集文件路径,并通过命令行将它们传递给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/"Reporting Static"/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:UsersmeDesktopPERLTESTmydump.log', Dumper( @latexcmdline );

在 exec 命令之前。

更新

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

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

正如我之前所说,问题是perltex本身不能正确处理包含空格的路径,即使它们作为@ARGV的单个元素正确传递。我相信解决方案是传递包含引号的路径,尽管我从未能够正确测试这一点,因为我没有安装 LaTex

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

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

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

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

我已经将相同的原则应用于$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. 弄清楚如何将正确的参数放入外部程序。
  2. 弄清楚如何从Perl程序中做到这一点。

对于第 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 根本不参与。

最新更新