在shell脚本中使用Perl一行命令

3

这个问题已经让我苦恼了好几个小时了。我写了一个简单的Perl一行代码封装器来更新一些DNS区域文件中的序列号。

我觉得有必要补充说明:不要提供其他方法来解决这个问题,好吗?这是关于为什么这种方法行不通,而不是如何通过其他方式实现结果。

这是我的简单脚本:

#!/bin/bash
#loop through the supplied files updating the timestamp (serial)
SERIAL=`date +%Y%m%d%H%M`;
for name in $@
do
saCMD="'s/^(\W*)\d*.*;\W*serial/\${1}$SERIAL ; serial/g'"
#echo the command 
echo "perl -pi -e "$saCMD" $name"
#execute the command
`perl -pi -e $saCMD $name`
done
  

我尝试了多种不同的方法,但它要么默默地失败,要么显示以下信息:

“Can't find string terminator "'" anywhere before EOF at -e line 1.”

如果我执行回显的命令,它就能完美运行。

我在Debian 7系统上。

有人可以指出为什么它没有按照我预期的执行吗?

编辑:



一些示例数据。
$TTL 300
domain.org. IN SOA     ns1.domain.com. admin.domain.org. (
                2014090914      ; serial, todays date+todays
                7200            ; refresh, seconds
                7200            ; retry, seconds
                2419200         ; expire, seconds
                3600 )          ; minimum, seconds        

需要注意的是这一行:2014090914 ; 串行号,今天的日期+今天


有趣的是 - 如果省略反引号,它是否有效?我问的原因是 - 反引号将生成一个子shell,我认为可能会解开另一层引号。 - Sobrique
反引号没有明显的区别。 - DeveloperChris
奇怪。因为像那样内联反引号应该会执行那个perl命令的输出。 - Sobrique
它不是只将输出返回给脚本吗?就像我格式化时间戳的方式一样?由于Perl没有返回错误,因此没有输出。这个相同的命令在Centos上完美地工作。 (虽然我最近没有测试过) - DeveloperChris
2个回答

2

至少存在引号问题。你将单引号作为saCMD="'s...'"的一部分。它们不会被Shell删除,而是传递给Perl,正如你在echo输出中看到的那样。

此外,

#execute the command
`perl -pi -e $saCMD $name`

可能有无用的反引号。或者你也想运行由perl脚本输出的命令?要调试shell脚本,请在开头加上set -x

这里可以正常工作:

#!/bin/bash
SERIAL=$(date +%Y%m%d%H%M)
for name in "$@"; do
  saCMD="s/^(\W*)\d*.*;\W*serial/\${1}$SERIAL ; serial/"
  perl -pi -e "$saCMD" "$name"
done

并将您的示例数据转换为
$TTL 300
domain.org. IN SOA     ns1.domain.com. admin.domain.org. (
                201508201330 ; serial, todays date+todays
                7200            ; refresh, seconds
                7200            ; retry, seconds
                2419200         ; expire, seconds
                3600 )          ; minimum, seconds

我知道反引号大多是多余的。我想知道这是否会改变命令的解释方式。单引号是为了传递给 Perl 应用程序作为 '脚本' 执行的。如果没有单引号,它将被视为多个变量。 - DeveloperChris
谢谢你提供 set -x 的提示。它输出了命令,如果我直接在命令行上运行它,它是有效的。但是在 shell 脚本中却不行。 - DeveloperChris
好的,我现在明白发生了什么。由于它被引用了两次,Perl没有将其识别为正则表达式。耶。 - DeveloperChris

0

适当的引用应该会有帮助。你还没有展示输入数据,所以我无法进行测试:

saCMD="s/^(\W*)\d*.*;\W*serial/\${1}$SERIAL ; serial/g" # No inner quotes.
perl -pi -e "$saCMD" "$name"

此外,/g 似乎毫无意义,因为正则表达式只匹配字符串的开头(^)。

我已经尝试了多个不同的正则表达式,最初它没有匹配开头。令人困惑的是,如果我回显该命令并直接从命令行运行它,它会完全按照预期工作。 - DeveloperChris
@DeveloperChris 这是因为在剪切/粘贴回显命令时,您第二次将其传递给 shell,shell 再次执行引号删除并在调用 perl 之前去除单引号。这就像 echo "''"echo '' 的区别。 - Jens
@Jens 是的,它确实会去掉单引号,但只有在将字符串分配给参数后才会这样做,否则该字符串将被分解为多个参数。无论脚本是从shell脚本中调用还是从命令行中调用,它仍然需要引号。我已经尝试了两种方式以确保。 - DeveloperChris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接