I am trying to execute the following gpg command from within Perl:
`gpg --yes -e -r me#mydomain.com "$backupPath\\$backupname"`;
However I get the following error:
Global symbol "#mydomain" requires explicit package name (did you forget to declare "my #mydomain"?)
Obviously I need to escape the '#' symbol but don't know how. How do I execute this command in Perl?
When you do:
`gpg --yes -e -r me#mydomain.com "$backupPath\\$backupname"`;
perl sees the #mydomain part and assumes you want to interpolate the #mydomain array right into the string.
But since there was no #domain array declared, it gives you the error.
The fix is simple: To tell perl that you want to treat #mydomain as a string and not as an array, simply put a backslash (\) before the #, like this:
`gpg --yes -e -r me\#mydomain.com "$backupPath\\$backupname"`;
Backticks run process in sub shell (slower, consumes more resources) and have some issues which you should investigate.
Following code demonstrates other approach which does not spawn sub shell.
use strict;
use warnings;
use feature 'say';
my $backupPath = '/some/path/dir';
my $backupname = 'backup_name';
my $command = "gpg --yes -e -r me\#mydomain.com $backupPath/$backupname";
my #to_run = split ' ', $command;
system(#to_run);
Handling backticks in Perl
Problem with backticks in multi-threaded Perl script on Windows
Related
perl -e 'print(123, #ARGV);' a b
# 123ab
perl -e 'print(123, #ARGV);' --help
# prints Perl's help instead
This is a toy example demonstrating the problem. In my real use-case I'm using -e to execute a large script from an embedded interpreter using perl_parse(...) function, the script has its own processing of --help switch, so I'd like to block any special processing of command line arguments after -e.
Is it possible?
Use a double hyphen to stop argument processing:
$ perl -e'print "[#ARGV]\n"' -- --help
[--help]
$
I want to run shell command as follow in perl:
tar --exclude="*/node_modules" \
--exclude="*/vendor" \
--exclude='.git' \
-zvcf /tmp/robot.tgz .
But it seems perl can not excute this:
`tar --exclude="cv/node_modules" \
--exclude="*/vendor" \
--exclude='.git' \
-zvcf /tmp/robot.tgz .`;
Here is the error:
tar: Must specify one of -c, -r, -t, -u, -x
sh: line 1: --exclude=*/vendor: No such file or directory
sh: line 2: --exclude=.git: command not found
sh: line 3: -zvcf: command not found
it seems perl treat each line as one command.
Update
I apologise. My original diagnosis was wrong
It is hard to clearly express in a post like this the contents of strings that contain Perl escape characters. If anything below is unclear to you then please write a comment to say so. I hope I haven't made things unnecessarily complicated
My original solution below is still valid, and will give you better control over the contents of the command, but my reasons for why the OP's code doesn't work for them were wrong, and the truth offers other resolutions
The problem is that the contents of backticks (or qx/.../) are evaluated as a double-quoted string, which means that Perl variables and escape sequences like \t and \x20 are expanded before the string is executed. One of the consequences of this is that a backslash is deleted if it is followed by a literal newline, leaving just the newline
That means that a statement like this
my $output = `ls \
-l`;
will be preprocessed to "ls \n-l" and will no longer contain the backslash that is needed to signal to the shell that the newline should be removed (or indeed to get the command passed to the shell in the first place)
Apart from manipulating the command string directly as I described in my original post below, there are two solutions to this. The first is to escape the backslash itself by doubling it up, like this
my $output = `ls \\
-l`;
which will prevent it from being removed by Perl. That will pass the backslash-newline sequence to the shell, which will remove it as normal
The other is to use qx'...' instead of backticks together with single-quote delimiters, which will prevent the contents from being processed as a double-quoted string
my $output = qx'ls \
-l';
This will work fine unless you have used Perl variables in the string that you want to be interpolated
Original post
The problem is that the shell removes newlines preceded by backslashes from the command string before executing it. Without that step the command is invalid
So you must do the same thing yourself in Perl, and to do that you must put the command in a temporary variable
use strict;
use warnings 'all';
my $cmd = <<'END';
tar --exclude="*/node_modules" \
--exclude="*/vendor" \
--exclude='.git' \
-zvcf /tmp/robot.tgz .
END
$cmd =~ s/\\\n//g;
my $output = `$cmd`;
There is no need for the backslashes of course; you can simply use newlines and remove those before executing the command
Or you may prefer to wrap the operations in a subroutine, like this
use strict;
use warnings 'all';
my $output = do_command(<<'END');
tar --exclude="*/node_modules" \
--exclude="*/vendor" \
--exclude='.git' \
-zvcf /tmp/robot.tgz .
END
sub do_command {
my ($cmd) = #_;
$cmd =~ s/\\\n//g;
`$cmd`;
}
#!/bin/bash
export IPV6=$1
expanded_ipv6_addr=`perl -e 'require "/usr/bin/ipv6_helper.pm"; $expand_ipv6=expand_ipv6_addr($ENV{IPV6});print $expand_ipv6'`
I don't want to export the $IPV6 variable, so I am looking any other way to do this.
Grab the value from #ARGV:
expanded_ipv6_addr=$(
perl -e '
require "/usr/bin/ipv6_helper.pm";
print expand_ipv6_addr(shift)
' "$IPV6"
)
Instead of exporting $1 into an environment variable you could use it again later and escape the perl code.
The following worked for me with a stubbed out version of /usr/bin/ipv6_helper.pm
#!/bin/bash
IPV6=$1
expanded_ipv6_addri=`perl -e "
require \"/usr/bin/ipv6_helper.pm\";
\\$expand_ipv6 = expand_ipv6_addr($IPV6);
print \\$expand_ipv6
"`
Suppose you've got this C shell script:
setenv MYVAR "-l os="\""redhat4.*"\"" -p -100"
setenv MYVAR `perl -pe "<perl>"`
Replace with code that will either replace "-p -100" with "-p -200" in MYVAR or add it if it doesn't exist, using a one liner if possible.
The topic does not correspond to content, but I think it may be usefull if someone posts an answer to topic-question. So here is the perl-oneliner:
echo "my_string" | perl -pe 's/my/your/g'
What you want will look something like
perl -e' \
use Getopt::Long qw( :config posix_default ); \
use String::ShellQuote; \
GetOptions(\my %opts, "l=s", "p=s") or die; \
my #opts; \
push #opts, "-l", $opts{l} if defined($opts{l}); \
push #opts, "-p", "-100"; \
print(shell_quote(#opts)); \
' -- $MYVAR
First, you need to parse the command line. That requires knowing the format of the arguments of the application for which they are destined.
For example, -n is an option in the following:
perl -n -e ...
Yet -n isn't an option in the following:
perl -e -n ...
Above, I used Getopt::Long in POSIX mode. You may need to adjust the settings or use an entirely different parser.
Second, you need to produce csh literals.
I've had bad experiences trying to work around csh's defective quoting, so I'll leave those details to you. Above, I used String::ShellQuote's shell_quote which produces sh (rather than csh) literals. You'll need to adjust.
Of course, once you got this far, you need to get the result back into the environment variable unmangled. I don't know if that's possible in csh. Again, I leave the csh quoting to you.
I have several bash scripts that need to be modified and I would very much prefer to not do it by hand... basically, they all contain the line
for ((i=${BEGIN} ; i < ${END} ; i++))
and I need to change this to
for ((i=${BEGIN}-1 ; i < ${END} ; i++))
the i=${BEGIN} is unique and appears only once in each file, so I figured I could search and replace it using a simple perl command. What I came up with is
> perl -w -i -p -e "s/i=\$\{BEGIN\}/i=\$\{BEGIN\}-1/" Script.sh
which results in the following error
syntax error at -e line 1, near "{BEGIN"
syntax error at -e line 1, near "}continue"
Execution of -e aborted due to compilation errors.
What is the syntax error here?
Thanks!
Tsadkiel
Use apostrophes instead of double quotes:
perl -w -i -p -e 's/i=\$\{BEGIN\}/i=\$\{BEGIN\}-1/'
This way, backslashes aren't removed by shell, so perl sees them and they escape what they should escape.
The bash shell is performing interpolation on the argument "s/i=\$\{BEGIN\}/i=\$\{BEGIN\}-1/" before it gets to Perl. Let's see how that might work:
$ echo "s/i=\$\{BEGIN\}/i=\$\{BEGIN\}-1/"
s/i=$\{BEGIN\}/i=$\{BEGIN\}-1/
The substitution s/i=$\{BEGIN\}/i=$\{BEGIN\}-1/ is going to be a problem in Perl because Perl will treat the sequence $\{ as the start of a lookup on the hash variable %\, but it will fail to compile because it won't find an (unescaped) closing brace. So what you really want Perl to see is something like
s/i=\$\{BEGIN\}/i=\$\{BEGIN\}-1/
And there are at least two ways to change your original command-line to accomplish this:
Escape the dollar signs:
perl -wpi -e "s/i=\\\$\{BEGIN\}/i=\\\$\{BEGIN\}-1/"
Prefer single quotes, which aren't interpolated in bash:
perl -wpi -e 's/i=\$\{BEGIN\}/i=\$\{BEGIN\}-1/'