通过ssh执行perl替换[来自shell脚本的perl one-oneliner]

时间:2017-03-01 20:27:02

标签: perl shell ssh

我试图远程执行此脚本:

perl -i -pe 's/nginx-cache\K(\d+)/ ++($n = $1) /e; s/MYSITE\K(\w+)/ ++($n = $1) /e;' $SITENAME

使用以下解决方案:

ssh -T root@$IPSRV <<EOI
perl -i -pe 's/nginx-cache\K(\d+)/ ++($n = $1) /e; s/MYSITE\K(\w+)/ ++($n = $1) /e;' /etc/nginx/sites-available/$SITENAME"
exit
EOI

我也试过没有&#34; -T&#34; ssh的选项

ssh root@$IPSRV "
> perl -i -pe 's/nginx-cache\K(\d+)/ ++($n = $1) /e; s/MYSITE\K(\w+)/ ++($n = $1) /e;' /etc/nginx/sites-available/$SITENAME"

但不幸的是它不起作用:

syntax error at -e line 1, near "( ="
syntax error at -e line 1, near "( ="
Execution of -e aborted due to compilation errors.

有人可以建议我远程运行此命令的解决方案吗? 提前谢谢!

请注意,$SITENAME是本地计算机上的变量。

[编辑]

根据@ ikegami的回答,我取得了一些进展。 我试过了

root@$IPSRV 'perl -i -pe'\''s/nginx-cache\K(\d+)/ ++($n = $1) /e; s/MYSITE\K(\w+)/ ++($n = $1) /e;'\'' /etc/nginx/sites-available/"$SITENAME"'
root@192.168.1.100's password: 
Can't do in place edit: /etc/nginx/sites-available/ is not a regular file.

我认为这与$ SITENAME变量的缺失替换有关。

要记住的另一个重要事项是在ssh root @ IPSRV之后使用单引号 - 它应该用引号替换,因为我在脚本中有其他变量,如果我使用单引号它们不会被翻译。 例如:

ssh root@$IPSRV "mkdir $TESTDIR
    cp /tmp/file $TESTDIR"

这样可行,但如果我尝试使用单引号:

ssh root@$IPSRV 'mkdir $TESTDIR
        cp /tmp/file $TESTDIR'

它没有。因此,如果运行perl替换的唯一方法是&#39; perl -i -pe&#39;&#39;&#39;&#39;&#39;&#39;&#39;

,我还必须考虑这个方面。

谢谢!

3 个答案:

答案 0 :(得分:5)

不正确的逃避。

ssh root@$IPSRV "...++($n = $1)..."...++( = )...传递给远程主机。与here-doc版本相同。 (here-doc版本也有一个流浪的引用。)

处理多个转义级别很复杂,所以让我们以编程方式进行转义。这也允许我们传递变量中的值,因为它们需要转换为shell文字。

quote() {
    prefix=''
    for p in "$@" ; do
        printf "$prefix"\'
        printf %s "$p" | sed "s/'/'\\\\''/g"
        printf \'
        prefix=' '
    done
}

ssh root@$IPSRV "$( quote perl -i -pe'...' "$SITENAME" )"

quote() {
    perl -MString::ShellQuote=shell_quote -e'print(shell_quote(@ARGV))' "$@"
}

ssh root@$IPSRV "$( quote perl -i -pe'...' "$SITENAME" )"

如果它对其他人有帮助,下面将介绍如何使用远程机器的$SITENAME var:

quote() {
    prefix=''
    for p in "$@" ; do
        printf "$prefix"\'
        printf %s "$p" | sed "s/'/'\\\\''/g"
        printf \'
        prefix=' '
    done
}

ssh root@$IPSRV "$( quote perl -i -pe'...' )"' "$SITENAME"'

quote() {
    perl -MString::ShellQuote=shell_quote -e'print(shell_quote(@ARGV))' "$@"
}

ssh root@$IPSRV "$( quote perl -i -pe'...' )"' "$SITENAME"'

ssh localhost sh <<'EOI'       # Notice the quotes around the token.
perl -i -pe'...' "$SITENAME"
EOI

或者,因为它不需要任何局部变量,所以您可以轻松地手动完成。使用远程命令,将每个'替换为'\'',然后用引号括起整个。

远程命令:

perl -i -pe'...' "$SITENAME"

本地命令:

ssh root@$IPSRV 'perl -i -pe'\''...'\'' "$SITENAME"'

答案 1 :(得分:0)

手工引用很难发生错误。让Perl为您完成所有工作:

use Net::OpenSSH;

my $one_liner = <<'EOOL';
s/nginx-cache\K(\d+)/ ++($n = $1) /e; s/MYSITE\K(\w+)/ ++($n = $1) /e
EOOL

my $ssh = Net::OpenSSH->new("root\@$ENV{IPSRV}");
$ssh->system('perl', '-i', '-pe', $one_liner, $ENV{SITENAME});
$ssh->die_on_error;

不要忘记export $IPSRV$SITENAME

答案 2 :(得分:0)

经过多次尝试后我才开始工作! ; - ))

问题与在将所有这些发送到远程SSH之前尝试扩展$ n或$ 1环境变量的shell有关。在远程端脚本变成:

perl -i -pe 's/nginx-cache\K(\d+)/ ++( = ) /e; s/MYSITE\K(\w+)/ ++( = ) /e;'

在“(=)”位置产生错误。 只需将它们转义为\ $ n就不会发送这些字符串:

perl -i -pe 's/nginx-cache\\K(\\d+)/ ++(\$n = \$1) /e; s/MYSITE\\K(\\w+)/ ++(\$n = \$1) /e;' $SITENAME

所以,完整的答案:

ssh root@IPSRV "
  first command
  second command
  perl -i -pe 's/nginx-cache\\K(\\d+)/ ++(\$n = \$1) /e; s/MYSITE\\K(\\w+)/ ++(\$n = \$1) /e;' $SITENAME
  "

感谢任何人指出我正确的方向!