Calling a perl script as a shell command - perl

I want to call my perl script as a command from command line.
Example lets say I have a perl file like following
#!/usr/local/bin/perl -w
#args =("mvn","package");
system(#args) == 0
or die "system #args failed"
I right not call this using package.pl
I tried doing the following
#!/bin/sh
eval 'exec /bin/perl –x -S $0 ${1+"$#"}'
if 0;
#!/usr/local/bin/perl -w
#args =("mvn","package");
system(#args) == 0
or die "system #args failed"
and then name the file 'package' .Do chmod on package
When I try to run package, then I get the error
"Can't open perl script []x:No such file or directory
Can someone please point me out , as to how to do this properly??
Thanks
Neeraj

Change to the name you want to use and make it executable:
cp package.pl package
chmod +x package
Run it:
package
or:
./package

Changed single quotes to double quotes and escaped inner double quotes. Also, there seems to be some problem with paths. Try calling your script with absolute path. I tried adding "./" and it worked:
#!/bin/sh
echo "This is shell"
eval "exec /usr/bin/perl -x -S ./$0 ${1+\"$#\"}"
if 0;
#!/usr/bin/perl -w
print "This is Perl\n";

Related

Is there a way to execute a file and one line of program in perl?

I want to execute some code before execution(redirect stderr to stdout).
perl -e "BEGIN {open STDERR, '>&STDOUT'}" perl.pl
But when there is -e, no file will be executed. I know $Config{sitelib}/sitecustomize.pl can pre-execute some code, and -f option can disable it. But this way is inflexible. In most cases, I do not need to add extra code. I don't want to add -f every time.
I cannot use shell to redirect. I want to set org-babel-perl-command in emacs org mode so that stdout and stderr can be printed in the same way, instead of opening another window to print stderr. org-babel-perl-command should be like perl.
For example, org-babel-python-command can be set to python -i -c "import sys; sys.stderr = sys.stdout".
perl -e'
open( STDERR, ">&STDOUT" );
do( shift( #ARGV ) );
' perl.pl
(Error handling needed.)
For the case in question, the following would suffice:
perl perl.pl 2>&1
Maybe even
./perl.pl 2>&1
You could just make a wrapper for perl. For example:
#!/bin/bash
exec perl "$#" 2>&1
Then make it executable and use instead of perl in your org-babel-perl-command. Ensure it can be found in your PATH or use full location.

Get full call of Perl script with parameters

How can I get the file name and input parameters of the script into a variable?
So it should look like this:
# Start script as such: ./myscript.pl -d -s server1.domain
#!/usr/bin/perl
use strict;
use warnings;
my $call = some_command;
print $call; # Output: myscript.pl -d -s server1.domain
# OR ./myscript.pl -d -s server1.domain
# OR /path/to/myscript.pl -d -s server1.domain
Tried doing this with __FILE__ and $0 but I can't seem to get the input parameters in the variable.
I'm running v5.10.1 on a AIX machine.
The program and its args are found in $0 and #ARGV respectively.
You can use String::ShellQuote's shell_quote to form a command line from them.

Conditional Perl Shebang?

I have a situation where I need to detect if a particular perl executable, /usr/goofy/bin/perl exists and if so use it to run the Perl script otherwise use /usr/bin/perl.
I have been struggling with this small POC script, called perlshebang.pl:
#!/bin/sh -e
perls="/usr/goofy/bin/perl /usr/bin/perl"
for pl_exec in $perls
do
if [ -x $pl_exec ]; then
exec "$pl_exec -w -S \"$0\" ${1+\"$#\"}"
fi
done
print "[$^X] Whoop!\n";
When I run this on a system that does not have /usr/goofy/bin/perl I get this error message:
./perlshebang.pl: 6: exec: /usr/bin/perl -w -S "./perlshebang.pl" : not found
And when I run it on a system that does have /usr/goofy/bin/perl I get a similuar error message:
./perlshebang.pl: line 6: /usr/goofy/bin/perl -w -S "./perlshebang.pl" : No such file or directory
I think I am close but cannot figure out why I am getting these error messages.
Thanks!
To answer your question, "Why am I getting these error messages?", the problem is your exec line:
exec "/path/to/cmd arg arg"
# This will attempt to execute a file named "cmd arg arg"
# (with spaces in name) in directory /path/to/
Contrast that with
exec /path/to/cmd arg arg
# This will attempt to execute a file named "cmd" in directory
# /path/to/, with arguments "arg" and "arg"
So, that is why the shell complains that it cannot find your executable. You don't have a file named perl -w -s "perlshebang.pl", neither under /usr/bin/ nor under /usr/goofy/bin/.
This sounds a little ugly to me if you are releasing software that uses this hack
If you have no other choice, then I suggest you make sure there is always a /usr/goofy/bin/perl, and use the shebang line
#!/usr/goofy/bin/perl
on all your scripts.
For those systems where you want to use the system perl, just make /usr/goofy/bin/perl a symlink to /usr/bin/perl
A co-worker of mine came up with this. I am not sure I fully understand it but it seems to work fine:
#!/bin/sh
#! -*-perl-*-
eval ' if test -x /usr/goofy/bin/perl ; then
exec /usr/goofy/bin/perl -x -S $0 ${1+"$#"};
elif test -x /usr/bin/perl ; then
exec /usr/bin/perl -x -S $0 ${1+"$#"};
fi '
if $running_under_some_shell;
use strict;
use warnings;
print "hello world\n"; # if $foo;
printf("running %s v(%vd)\n", $^X, $^V);
__END__
unpod like docs.
See http://perldoc.perl.org/perlrun.html
You can run the idea out of a Perl script running /usr/bin/perl. Use the shebang line with the 'goofy perl' in your script that should run. Then run the following wrapper, followed by the normal invocation of the script (its name and arguments).
#!/usr/bin/perl
exec "#ARGV";
exec "/usr/bin/perl #ARGV";
print STDERR "Couldn't execute either.\n";
Let's call the above pick_perl.pl, and your script is script.pl. Run it all as
pick_perl.pl script.pl args-for-script
The exec replaces the running program altogether with the one it executes, ie. it loads the new program. Thus your script runs with its own shebang. If that failes exec returns quietly (with false) and the next statement is executed so the other Perl runs the script (overriding the shebang). This happens if script's shebang fails, but also if the first exec fails to execute for any reason.
If you wish/need to run checks then put exec in a full if block. One can also interrogate the 'goofy_perl' file further if -e isn't assuring enough.
#!/usr/bin/perl
$system_perl = "/usr/bin/perl";
$goofy_perl = "/usr/goofy/bin/perl";
# Your 'goofy_perl' script with its arguments
#script_cmd = #ARGV;
if (-x $goofy_perl) { exec "#script_cmd" }
exec "$system_perl #script_cmd";
The #script_cmd has the full command line for your script (which has 'goofy_perl' shebang).

System command in perl

I need to run a system command which would go to a directory and delete sub directories excluding files if present. I wrote the below command to perform this operation:
system("cd /home/faizan/test/cache ; for i in *\; do if [ -d \"$i\" ]\; then echo \$i fi done");
The command above keeps throwing syntax error. I have tried multiple combinations but still not clear how this should go. Please suggest.
Well, your command line does contain syntax errors. Try this:
system("cd /home/faizan/test/cache ; for i in *; do if [ -d \"\$i\" ]; then echo \$i; fi; done");
Or better yet, only loop over directories in the first place;
system("for i in /home/faizan/test/cache/*/.; do echo \$i; done");
Or better yet, do it without a loop:
system("echo /home/faizan/test/cache/*/.");
(I suppose you will want to rmdir instead of echo once it is properly debugged.)
Or better yet, do it all in Perl. There is nothing here which requires system().
You're still best off trying this as a bash command first. Formatting that properly makes it much clearer that you're missing statement terminators:
for i in *; do
if [ -d "$i" ]; then
echo $i
fi
done
And condensing that by replacing new lines with semicolons (apart from after do/then):
for i in *; do if [ -d "$i" ]; then echo $i; fi; done
Or as has been mentioned, just do it in Perl (I haven't tested this to the point of actually uncommenting remove_tree - be careful!):
use strict;
use warnings;
use File::Path 'remove_tree';
use feature 'say';
chdir '/tmp';
opendir my $cache, '.';
while (my $item = readdir($cache)) {
if ($item !~ /^\.\.?$/ && -d $item) {
say "Deleting '$item'...";
# remove_tree($item);
}
}
Using system
my #args = ("cd /home/faizan/test/cache ; for i in *; do if [ -d \"\$i\" ]; then echo \$i; fi; done");
system(#args);
Using Subroutine
sub do_stuff {
my #args = ( "bash", "-c", shift );
system(#args);
}
do_stuff("cd /home/faizan/test/cache ; for i in *; do if [ -d \"\$i\" ]; then echo \$i; fi; done");
As question title stand for system command, this will answer directly, but the sample command using bash contain only thing that will be simplier in perl only (take a look at other answer using opendir and -d in perl).
If you want to use system (instead of open $cmdHandle,"bash -c ... |"), the prefered syntax for execution commands like system or exec, is to let perl parsing the command line.
Try this (as you've already done):
perl -e 'system("bash -c \"echo hello world\"")'
hello world
perl -e 'system "bash -c \"echo hello world\"";'
hello world
And now better, same but letting perl ensure command line parsing, try this:
perl -e 'system "bash","-c","echo hello world";'
hello world
There are clearly 3 argument of system command:
bash
-c
the script
or little more:
perl -e 'system "bash","-c","echo hello world;date +\"Now it is %T\";";'
hello world
Now it is 11:43:44
as you can see in last purpose, there is no double double-quotes enclosing bash script part of command line.
**Nota: on command line, using perl -e '...' or perl -e "...", it's a little heavy to play with quotes and double-quotes. In a script, you could mix them:
system 'bash','-c','for ((i=10;i--;));do printf "Number: %2d\n" $i;done';
or even:
system 'bash','-c','for ((i=10;i--;));do'."\n".
'printf "Number: %2d\n" $i'."\n".
'done';
Using dots . for concatening part of (script part) string, there are always 3 arguments.

How can I check if ldconfig is in the PATH, using bash?

I have scrapped a perl snippet off the web for use in my bash scrip and for reasons too long to go into, it will be better if I could achieve what it tries to do directly in bash.
Here is the script:
bash stuff
...
perl <<'EOF'
use 5.006;
use strict;
use warnings;
if (! can_run("ldconfig")) {
die "you need to have ldconfig in your PATH env to proceed.\n";
}
# check if we can run some command
sub can_run {
my ($cmd) = #_;
#warn "can run: #_\n";
my $_cmd = $cmd;
return $_cmd if -x $_cmd;
return undef;
EOF
more bash stuff
Basically, the question could be rephrased as , "how can I check if ldconfig is in the PATH env using bash?"
You want bash's builtin type command:
if type -P ldconfig; then
echo "ldconfig is in the PATH"
else
echo "ldconfig is not in the PATH"
fi
Expressed negatively:
if ! type -P ldconfig; then
echo "ldconfig is not in the PATH"
fi
A more straightforward solution would be to invoke the shell and the which command:
$path = `which ldconfig`;
if ($path) {
...
}
If ldconfig is recognised, the path to its executable will be returned, empty output otherwise.
Or, if this Perl script is not going to do anything more than that, you can dismiss it and execute the same command from bash.
I refined #glenn jackman's answer to make it "quiet". It worked as it was but it output "/sbin/ldconfig" to the screen in addition to the echo when in the path. With this modification, only the echo is output:
type ldconfig &>/dev/null
if [ "$?" -eq 0 ]
then
echo "ldconfig is in the PATH"
else
echo "ldconfig is not in the PATH"
fi
Thanks to all.