Reputation: 123
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!
Upvotes: 1
Views: 641
Reputation: 123
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!
Upvotes: 0
Reputation: 10244
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
.
Upvotes: 0
Reputation: 385829
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"'
Upvotes: 5