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

Posted

技术标签:

【中文标题】如何让 perl 正确传递具有多个参数和复杂文件路径(空格和符号)的命令行参数?【英文标题】:How can I get perl to correctly pass a command line argument with multiple arguments and complex file paths (spaces and symbols)? 【发布时间】:2016-07-06 09:00:12 【问题描述】:

我有一个小的 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/"Reporting Static"/file.tex'(开头没有引号)重新输入路径,它随后被接受并运行。但最初传递它时这条路径不起作用,这表明某些内部 perltex 代码接受初始路径与在错误后重新传递相同路径不同......不太确定该怎么做。

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

    $VAR1 = [
      'pdflatex',
      '--jobname=--',
      '\\makeatletter\\def\\plmac@tagAYNNNUVKQVJGZKKPGPTH\\def\\plmac@tofilePerl.topl\\def\\plmac@fromfilePerl.frpl\\def\\plmac@toflagPerl.tfpl\\def\\plmac@fromflagPerl.ffpl\\def\\plmac@doneflagPerl.dfpl\\def\\plmac@pipePerl.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 命令之前。

【问题讨论】:

您显示的代码会将$texfile 中的字符串作为一个参数传递给perltex;实际上,您应该只使用$texfile 而不是"$texfile"。我猜perltex 不喜欢那个参数包含空格 在哪里可以获得您的perltex.pl 的副本? @borodin 你可以在这里获取 perltex.pl ctan.org/pkg/perltex 【参考方案1】:

更新

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

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

正如我之前所说,问题在于 perltex 本身不能正确处理包含空格的路径,即使它们作为 @ARGV 的单个元素正确传递也是如此。我相信解决方案是传递路径包括括起来的引号,尽管由于我没有安装 LaTex,所以我一直无法正确测试这一点

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

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

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

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

我对@9​​87654332@ 应用了相同的原则,因为它很可能也包含空格

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 的作者分发时,这才是一个可靠的修复。同时,我正在从调用方寻找更好的解决方案

【讨论】:

@Borodin 我明白这可能是个问题 xD。我试图快速应用这个临时修复,但它似乎没有采取。虽然我没有机会检查调用目录,因为我可能位于多个位置(我知道我不应该只是还没来得及清理)所以我一定会在明天先检查一下并希望届时能回复您。 @NzFrancis:请阅读我的更新。你需要Win32::ShellQuote 抱歉耽搁了,我想我需要检查我的通知设置。我已经清理了我的 perl 安装,尝试了您的修补程序,返回了原始 perltex 文件,安装了 Win::ShellQuote 模块,然后尝试了上面的代码,但它仍然无法找到正确的路径并且在空间处被切断。至少感谢您,我现在知道确切的问题,但我仍然看不出您的建议如何不起作用,因为根据您的代码,我可以看到它们为什么应该起作用...... @NzFrancis:我一直在使用的文件是perltex.pl。它的 MD5 校验和是bd69ec2ff1c121b18247ae0fa54420dc。您应该开始调试 perltex:这并不难。如果你想离开乐队,我可以告诉你。当乳胶本身在第 158 行用exec $latexcmdline[0] @latexcmdline 执行时,问题一定出在@latexcmdline 的内容上。只需将数组的内容转储到那里,我们就会更聪明 @Borodin 感谢您的时间和精力,我真的很感谢调试 perltex 的一些指针(我将从按照建议转储其内容开始)。虽然我不太确定如何获取自己的校验和,也不知道 MD5 是什么意思。【参考方案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"));

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

更新: Borodin 下面关于这对$texfile 没有影响的评论是准确的。我们将列表传递给 system() 的事实意味着根本不涉及 shell。

【讨论】:

额外的双引号不会对$texfile 产生任何影响——它们只是被剥离,目标程序永远不会看到它们——尽管它可能会改变$Filename 的处理方式。问题是对带有多个参数的system 的调用无论如何都不会通过shell 传递,因此这些值保留了它们的个性 我同意目标程序永远不会看到引号。但它会将$textfile 的内容视为单个参数。至少,我的测试似乎表明了这一点。 正如我所说,system 的多参数形式无论如何都应该使值保持独立和完整。您是否有测试显示它在空格上拆分值? @Dave Cross 我测试了你的两个建议都没有运气,我可以看到你编写的小脚本的实用性。我一直在手动将参数输入 cmd 以测试它,但即便如此,perltex 也不接受带空格的路径。它从来没有,但我测试了输出并且 perl 确实传递了所需的参数(就代码而言)只是这个参数仍然不被接受,这就是为什么我开始认为我的问题可能更相关到 perltex 比到 perl ..... @NzFrancis:我的测试程序只使用use Data::Dump; dd \@ARGV; :)

以上是关于如何让 perl 正确传递具有多个参数和复杂文件路径(空格和符号)的命令行参数?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 jdbcTemplate 中为具有相同值的多个参数标记传递参数?

如何将哈希传递给 Perl 中的函数?

如何在perl中验证具有给定文件扩展名的多个文件

如何将 select 语句的 IN 子句中的参数作为具有多个值的参数传递?

将两个或多个数组传递给 Perl 子例程

Excel:如何在 excel 中转义逗号以在另一个函数(具有多个参数)内传递一个函数(具有多个参数)