【问题标题】:How to use Shell Parameter Expansion in Perl?如何在 Perl 中使用 Shell 参数扩展?
【发布时间】:2020-08-13 02:40:55
【问题描述】:

我正在尝试实现 Bash 参数扩展技术来替换 Perl 脚本中文件的现有扩展名。

这是我用于替换文件扩展名的 bash 代码,它工作正常:

mv "$f" "${f%.*}.png"

另一方面,我正在尝试使用 system() 在 Perl 脚本中使用它:

system('mv', "$a/$fileName", "$a/${fileName%.*}".'.png');

$a 保存通过命令行参数传递的目录名称。

它抛出"syntax error near %",然后是"missing right curly or square bracket within string"。错误消息中没有 EOF。

当我不使用%.* 时,它只是在现有文件结束后附加.png,但它不会替换。一些帮助将不胜感激。

【问题讨论】:

  • 这能回答你的问题吗? How do I pass Shell parameters to Perl script?
  • system 的多参数形式根本不运行 shell,因此不支持任何 shell 语法。无论如何,当你拥有 Perl 时,为什么还要使用 shell PE? Perl 更具表现力。
  • 您好,感谢您提供的信息。我是 Perl 和 Bash 脚本的新手。我设法在 bash 中执行任务,但我无法在 Perl 中完成任务。有什么解决办法吗?
  • 首先,不能保证system 会使用bash,所以如果你想确保你有bash 执行代码,你应该明确地这样做。但是由于您确实在 Perl 中使用glob 函数进行了参数扩展(不是在 bash 中可用的全部范围,但至少在 POSIX shellx 中可用的范围),您可以考虑编写您的代码完全在 Perl 中,无需创建子进程。

标签: bash perl parameter-expansion


【解决方案1】:

所以您希望替换扩展名 -- 以更改文件名。一旦您在 Perl 脚本中,就没有理由为此使用 shell。

更改扩展很容易“手动”完成(请参阅下面的库)。使用正则表达式

my $new_filename = $filename =~ s/.*\.\K.*/$new_extension/r;

.* 贪婪地匹配到最后一个 .(在模式中),然后\K 丢弃所有匹配项,以便我们只替换它后面的内容。或者,使用字符串结尾锚$

my $new_filename = $filename =~ s/\.\K[^.]+$/$new_extension/r;

匹配并替换. 之后字符串末尾的所有非. 字符。 在这两种情况下,修饰符 /r 使它返回新字符串并保持原始字符串不变;没有它,字符串 ($filename) 将就地更改。

现在更改磁盘上文件的名称。一个标准工具是File::Copy

use File::Copy qw(move);

move $filename, $new_filename  or die "Can't move: $!";

(如果你注意到内置的rename 保持冷静并走过它。看到它的链接页面就可以了。)


一般来说,最可靠的方法当然是使用合适的库,并且有一些很好的库可以用于此目的。这是有用的Path::Tiny(您需要安装)

use Path::Tiny;

my $path = path($filename);

my $new_fqn = $path->parent->child( $path->basename(qr/[^.]+/) . $new_ext );

请参阅此模块的链接文档,但这里是对上述内容的简要说明。

方法parentPath::Tiny 对象的形式返回到最后一个组件(文件/目录)的路径。然后在其上调用的方法child 添加了另一个组件。

为此,我们得到basename(最后一个组件,在此使用中去掉了扩展名),然后将新的扩展名附加到它上面。瞧,我们得到了替换扩展名的整个路径。这确实会解析路径两次,一次用于parent,一次用于basename

这样做的另一个好处是该模块也有一个move 方法,所以

$path->move($new_fqn);

完成工作。

模块检查错误并检查croaks 或抛出its own class 异常。


旧的和尝试过的 UNIX 方法是使用核心 File::Basename

use File::Basename;

my ($name, $path, $ext) = fileparse($filename, qr/\.[^.]*/); 

my $new_fqn = "$path/$name.$new_ext";

然后使用File::Copy::move重命名文件。

【讨论】:

  • 感谢您的帮助。我将实施您的解决方案。请不要删除您的答案。非常感谢!
  • @LeonS.Kennedy 我只会添加它,还有更多方法可以做到这一点:)
  • 感谢您的努力和解释。它奏效了,终于。早些时候,当我将 move 函数括在括号中时,它给出了一个错误 "Useless use of private variable in void context" 然后我删除了括号并运行了。知道为什么它说“无用的使用”吗?无论如何,它奏效了。非常感谢!!!
  • @LeonS.Kennedy hm ...您“附上”的准确程度如何?它当然应该与parens一起使用(我只是觉得没有它们更干净,这就是我将它们排除在外的原因)。感谢您的贡献——敬请期待,我即将添加...
  • 起初我确实喜欢move ("$a/$fileName", "$a/$new_filename" or die "Can't move: $!");,除了“无用的使用”错误,它还说Usage: move(FROM, TO)错误。然后我删除了括号,它就像一个魅力。 :)
【解决方案2】:

不要同时使用 Bash 和 Perl 语法,这两种语言都有晦涩难懂的语法,同时使用它们会使事情变得更糟。

重击:

"${f%.*}.png"

Perl: 使用 File::Basename 模块https://perldoc.perl.org/File/Basename.html

$f_new=fileparse($f_old,qr/\.[^.]*/);
$f_new.=".png";

重击:

mv

Perl:

rename / File::Copy (see comment)

放在一起(单行 Perl 中的演示): 将 foo.jpg 重命名为 foo.png

使用重命名:

$ perl -MFile::Basename -e '$f_old=$ARGV[0];$f_new=fileparse($f_old,qr/\.[^.]*/);$f_new.=".png";rename $f_old, $f_new' foo.jpg

使用文件::复制

$ perl -MFile::Copy -MFile::Basename -e '$f_old=$ARGV[0];$f_new=fileparse($f_old,qr/\.[^.]*/);$f_new.=".png";File::Copy::move $f_old, $f_new' foo.jpg

【讨论】:

  • 感谢您的回答。我是 Perl 新手,我正在理解您的答案并一点一点地实现它。
  • 使用 (fileparse($f_old,qr/\.[^.]*/);) 只返回裸名(不带扩展名),不带路径。这不利于重命名文件。另外,我建议不要使用rename - 太多细节无法解释。请参阅文档。
  • @zdim 感谢您的建议,我更新了我的答案。我有一段时间没有使用 Perl,似乎 TIMTOWTDI 的日子已经过去了:)
  • 一个有趣的观点!当然,虽然 TIMTOWDI 还活着而且很好,但我想说的是,现在人们确实会区分某些方式比其他方式更好。 (注:方式s。复数:)
  • @zdim 同意!和perlers总是善良:)
【解决方案3】:

您在 Perl 中有双引号字符串,这意味着 $variables 被插入到 Perl 中。就像用 Perl 写的一样

my $xx="abc"
my $yy=${xx%.*}

你会得到同样的错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-29
    • 1970-01-01
    • 2015-06-18
    • 1970-01-01
    • 2019-11-07
    相关资源
    最近更新 更多