system("echo -e $ str")无法在perl中正确输出

use*_*254 0 perl system echo

我写了一个简单的perl脚本

#!/usr/bin/perl
my $str = "hello\nworld";
my $cmd = "echo -e $str";
system($cmd);
Run Code Online (Sandbox Code Playgroud)

输出是:

-e hello
sh: 2: world: not found
Run Code Online (Sandbox Code Playgroud)

这不是预期的.我想要的是这个:

hello
world
Run Code Online (Sandbox Code Playgroud)

注意"echo -e"可以正常工作,因为当我运行以下命令时,

echo -e "hello\nworld"
Run Code Online (Sandbox Code Playgroud)

输出是正确的.

任何解决方案

Mar*_*eed 5

如果您$cmd在设置后打印出来,您会看到:

echo -e hello
world
Run Code Online (Sandbox Code Playgroud)

...如果你将它粘贴到shell中,你会收到类似的错误消息,尽管没有-e.

第一个问题是当它到达shell时,字符串缺少引号和非文字换行符.试试这个:

my $str = "hello\\nworld";
my $cmd = "echo -e '$str'";
Run Code Online (Sandbox Code Playgroud)

在这种特殊情况下,你可以只使用引号 - 在参数内部会有一个文字换行符echo,但只要它被引用就是合法的.

但是,您仍然会-e在输出中获得文字,而不会将其解释\n为换行符.那是因为perl正在运行/bin/echo,而不是echo内置的bash.如果你想使用bash的内置,你必须告诉perl bash明确地运行.但这意味着将echo命令本身传递给bash中的参数,这引入了另一个引用级别来处理.

这里(和所有地方,真的)最好的选择是使用多参数版本system; 绕过第一个shell级别并直接执行命令.我们仍然需要处理换行符:

my $str = "hello\nworld";                 # literal newline
(my $escaped_str = $str) =~ s,\n,\\n,g;    # escaped newline
Run Code Online (Sandbox Code Playgroud)

为了从shell中获得所需的行为,我们需要在shell命令中将字符串嵌入单引号中,因为单引号中的任何内容都完全由shell完成.在这种情况下,我们只是从Perl代码中的文字中分配了字符串,因此我们确切地知道它包含的内容,并且它不包含撇号.但是如果它可能包含撇号,我们应该通过另一个转义传递它来引用它们,所以嵌入单引号是安全的:

$escaped_str =~ s,','\\'',g;              # quote any embedded apostrophes
Run Code Online (Sandbox Code Playgroud)

您可能还希望用转义符替换其他字符 - 也许是制表符或回车符.这也是进行这些改变的地方.

使用转义字符串构建bash命令:

my $command = "echo -ne '$escaped_str'";  # bash command to echo string
Run Code Online (Sandbox Code Playgroud)

做这样的手动搜索/替换容易出错; 有很多边缘案例需要考虑.因此,您可能还需要查看CPAN模块String::ShellQuote,在该模块中,您可以跳过上面的手动转义并从原始文件生成转义字符串,如下所示:

my $command = shell_quote("echo", "-ne", $str);
Run Code Online (Sandbox Code Playgroud)

获得转义命令字符串后,使用multiple-argument system直接使用该字符串作为参数运行bash:

system('bash', '-c', $command);
Run Code Online (Sandbox Code Playgroud)

总的来说,更重要的是,我会考虑将shell命令替换为Perl本身的代码; 结果将更容易管理,并且可能更便携和高性能.