1

I have a perl script from within which I need to execute a simple perl one-liner on a remote host:

ssh 192.168.1.1 "perl -pi.bup -e 's/^(\s+?kernel)(.*)(?<!audit=1)$/$1$2 audit=1/' /etc/grub.conf" 

This simply adds "audit=1" to the end of each kernel line in /etc/grub.conf if it does not already exist.

The one-liner works just fine when run directly on the host, but not when executed via ssh from within another perl script. I have tried to escape all dollar signs with one or more backslashes and I have also tried to escape the backslash in "\s", but nothing I do seems to work.

Note I do not want to copy a script to the remote host and then execute it - I would like to do it using the ssh command directly.

How to properly escape this so that it works?

-- Update 9/9/2015 to show exactly what I am doing in the perl script:

sub SomeMethod { &RunCommand($host, "perl -pi.bup -e \'s/^(\s+?kernel)(.*)(?<!audit=1)\$/\$1\$2 audit=1/\' /etc/grub.conf"); } sub RunCommand { my ($server, $command) = @_; my $commandOutput = ""; if ($server ne "") { $command = "ssh $server \"$command\""; } $commandOutput = `$command`; print $commandOutput; if (($? >> 8) != 0) { &LogMessage ("$command failed:\n\n$commandOutput"); return $commandOutput; } return $commandOutput; } 

-- Update #2, using system instead of back-ticks:

system 'ssh', $host, 'perl', '-pi.bup', '-e', 's/^(\s+?kernel)(.*)(?<!audit=1)$/$1$2 audit=1/', '/etc/grub.conf'; bash: -c: line 0: syntax error near unexpected token `(' bash: -c: line 0: `perl -pi.bup -e s/^(\s+?kernel)(.*)(?<!audit=1)$/$1$2 audit=1/ /etc/grub.conf' 

Ok, I can use system here, but how to properly escape it??

5
  • @c4f4t0r sed vs. perl is probably not the issue here - it is more a matter of properly escaping the needed characters. But if you can provide a sed command that will work when executed via ssh (as in my post) from within a perl script, I will happily use that instead. Commented Sep 9, 2015 at 21:17
  • Show exactly what you're doing in the perl script. Commented Sep 9, 2015 at 21:22
  • @glenn Ok, code added to post. Commented Sep 9, 2015 at 21:34
  • In my previous an missed one \ Commented Sep 9, 2015 at 21:55
  • Less related to your question, you may also want to look into the use of grubby, if that is an option for you. You may find it easier to add/remove/change grub options. Sorry, I know that isn't related to your question. Commented Sep 9, 2015 at 22:44

3 Answers 3

2

Since you're editing the remote file in place, you don't need to capture the output, so system would be preferable than using backticks:

system 'ssh', '192.168.1.1', 'perl', '-pi.bup', '-e', 's/^(\s+?kernel)(.*)(?<!audit=1)$/$1$2 audit=1/', '/etc/grub.conf'; 

Note that you don't have to pass the remote command as a single parameter to ssh. That helps the quoting a bit. If you really want to, you can:

system 'ssh', '192.168.1.1', q{perl -pi.bup -e 's/^(\s+?kernel)(.*)(?<!audit=1)$/$1$2 audit=1/' /etc/grub.conf}; 

Using the q{} operator to allow the perl one-liner to use single quotes.


I would do this, actually:

use Try::Tiny; use IPC::System::Simple qw{capture}; sub SomeMethod { my $output = RemoteCommand( $host, q{perl -pi.bup -e 's/^(\s+?kernel)(.*)(?<!audit=1)$/$1$2 audit=1/' /etc/grub.conf} ); } sub RemoteCommand { my ($server, $command) = @_; my $output; try { $output = capture('ssh', $server, $command); } catch { LogMessage("command failed: ($command) : $_"); $output = $_; }; return $output; } 
4
  • The RunCommand method is used by a large number of methods and I would like to not modify it, if possible (not to break other functionality). I was hoping for some guidance on how to escape the command in my original post. Commented Sep 9, 2015 at 21:38
  • Since backticks execute your command with sh -c 'your command', it will be very difficult to get the escaping right. I'd not bother. Make your implementation more robust. Commented Sep 9, 2015 at 21:48
  • Ok, I tried using "system" instead - please see my edit for the result. Still needs some escaping and I still have a hard time making it work... Commented Sep 9, 2015 at 22:22
  • system is a perl function, not a shell command. Commented Sep 10, 2015 at 2:02
0

I finally got it working.

From bash directly:

echo 's/^(\s+?kernel)(.*)(?<!audit=1)$/$1$2 audit=1/' | ssh 192.168.1.1 "perl -pi.bup - /etc/grub.conf" 

From perl script:

my $command = "echo 's/^(\\s+?kernel)(.*)(?<!audit=1)\$/\$1\$2 audit=1/' | ssh $_wsAddress 'perl -pi.bup - /etc/grub.conf' "; &RunCommand("", $command); 
0

I tried with the following simple command and it works

ssh localhost "sed -i.bck '/[^audit]/{s/\(^\s\+kernel.*\)/\1 audit=1/g}' /boot/grub/menu.lst" 

Isn't about sed vs perl, but using the previous command is more clear to archive what you want.

2
  • that almost works (provided that you use "\\1" instead of "\1") directly from bash. But it is only supposed to append "audit=1" if it is not already there - as it is now it appends it always. Also, it is only supposed to affect lines that start with the word kernel (preceeding spaces ok). I tried this, but it did not update the file: ssh localhost "sed -i.bck 's/\^(\s\+kernel.*)(?<!audit=1)\$/\\1 audit=1/g' /etc/grub.conf ; cat /etc/grub.conf" Commented Sep 9, 2015 at 22:13
  • 1
    @Zek I updated my sed command, Now the command works only when the line kernel doesn't have audit key word Commented Sep 10, 2015 at 0:08

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.