1

I'm trying to execute this script remotely:

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

with the following solution:

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

I tried also without the "-T" option of 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"

but unfortunately it does not work:

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

Could someone please suggest me a solution for running this command remotely? Thanks in advance!

Note that $SITENAME is a variable on the local machine.

[EDIT]

I have made some progress, based on the @ikegami's answer. I tried

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"'
[email protected]'s password: 
Can't do in place edit: /etc/nginx/sites-available/ is not a regular file.

I think it's related to the missing substitution of $SITENAME variable.

Another important thing to keep in mind is the use of single quote after ssh root@IPSRV - it should be replaced by quotes because I have other variables into the script and if I use single quote they are not translated. Example:

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

This works, but if I try with single quote:

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

it does not. So I have to consider also this aspect if the only way for running the perl substitution is 'perl -i -pe'\''s ...

Thanks!

8
  • 1
    Have you tried to quote the heredoc? See How to avoid heredoc expanding variables? Commented Mar 1, 2017 at 20:37
  • Yes, with -T option and it returns this error: stdin: is not a tty syntax error at -e line 1, near "( =" syntax error at -e line 1, near "( =" Execution of -e aborted due to compilation errors. Commented Mar 1, 2017 at 20:52
  • The third uses improper escaping. You are doing ssh root@$IPSRV "++($n = $1)" so are passing ...++( = ) to the remote host. Same issue with the second, actually. Commented Mar 1, 2017 at 20:55
  • Do you attempt the remote execution from shell script or perl script? Commented Mar 1, 2017 at 20:56
  • 1
    Possible duplicate of Here document: no parameter expansion Commented Mar 1, 2017 at 20:57

3 Answers 3

5

Improper escaping.

ssh root@$IPSRV "...++($n = $1)..." passes ...++( = )... to the remote host. Same with the here-doc version. (The here-doc version also has a stray quote.)

Handling multiple levels of escaping is complicated, so let's do the escaping programmatically. This also allows us to pass values from variables, as they need to be converted into shell literals.

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" )"

or

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

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

In case it's of help to others, the following shows how to use the remote machine's $SITENAME var instead:

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"'

or

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

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

or

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

Or, since it doesn't need any local variables, you can do it manually rather easily. Take the remote command, replace every ' with '\'', then wrap the whole with quotes.

Remote command:

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

Local command:

ssh root@$IPSRV 'perl -i -pe'\''...'\'' "$SITENAME"'
Sign up to request clarification or add additional context in comments.

4 Comments

Basically this script execute many commands remotely and this perl substitution is one of the last command.
All commands, needs to be running remotely. so ssh root@$IPSRV "first command second command third command ..." and this perl substitution it's included in the commands
to execute the substitution on a remote file
You're telling me your have multiple commands to run because you want to perform substitutions on a remote file? What??? You're not making any sense. Do you have some kind of problem with the answer?
0

Quoting by hand is hard an error prone. Let's Perl do all the work for you:

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;

Don't forget to export $IPSRV and $SITENAME.

2 Comments

Unfortunately this is not an option - the script is invoked by bash so if I want to use your solution I need to add the invocation of this perl script into the bash one. Thanks anyway!
... or you could rewrite the full script in Perl!
0

After a lot of tries I got it working! ;-))

The problem was related to the shell that tries to expand $n or $1 environmental variables prior to sending all this to remote SSH. And on remote side script turns into:

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

which yields error on "( = )" places. Just escaping them as \$n sends these string untouched:

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

So, the complete answer:

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
  "

Thanks anyone for pointing me in the right direction!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.