我写了一个简单的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)
输出是正确的.
任何解决方案
如果您$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本身的代码; 结果将更容易管理,并且可能更便携和高性能.