perl: sh: 1: Syntax error: Bad fd number - perl

I have a very simple Perl script that fails with this error message:
sh: 1: Syntax error: Bad fd number
Here is the script (two lines)
#!/usr/bin/perl
system("xterm >& /dev/null &");
If I run the same xterm command from the command-line, it works. From the Perl script, it doesn't. What is wrong?

system(EXPR)
is short for[1]
system("/bin/sh", "-c", EXPR)
In other words, it takes a bourne shell command.
xterm >& /dev/null &
isn't a valid bourne shell command. You want
xterm >/dev/null 2>&1 &
Maybe you used a different shell when you tested it outside of Perl.
Technically, it's closer to
use Config qw( );
system($Config::Config{sh}, "-c", EXPR)
Except in Windows.

Firstly, the preferred syntax for redirecting both stdout and stderr in Bash is &>, not >&, because the latter can be confused with other redirection forms.
Secondly, system uses /bin/sh which may behave differently than your default shell.
Try writing it out explicitly, as in
system("xterm >/dev/null 2>&1 &");
or skipping the shell altogether.
if (fork() == 0) {
open STDOUT, '>', '/dev/null';
open STDERR, '>&', *STDOUT;
exec "xterm";
POSIX::_exit();
}

Related

How to remove carriage return in Perl properly?

I have a code that looks like this
sub ConvertDosToUnix{
my $file = shift;
open my $dosFile, '>', $file or die "\nERROR: Cannot open $file";
select $dosFile;
print("Converting Dos To Unix");
`perl -p -i -e "s/\r//g" $dosFile`;
close $dosFile;
}
Also, the perl command works when I used that outside the subroutine or in the main function. But when I created a separate subroutine for converting dos to unix, I got an error that says:
sh: -c: line 0: syntax error near unexpected token `('
//g" GLOB(0x148b990)' -p -i -e "s/
In which I don't understand.
Also, I also tried dos2unix but for some reason, it doesn't totally remove all the carriage returns like the perl command.
Honestly, you seem a little confused.
The code you have inside backticks is a command that is run by the shell. It needs to be passed a filename. You have your filename in the variable $file, but you pass it the variable $dosFile which contains a file handle (which stringifies to "GLOB(0x148b990)" - hence your error message).
So all your work opening the file is wasted. Really, all you wanted was:
`perl -p -i -e "s/\r//g" $file`
But your system almost certainly has dos2unix installed.
`dos2unix $file`
I should also point out that using backticks is only necessary if you want to capture the output from the command. If, as in this case, you don't need the output, then you should use system() instead.
system('dos2unix', $file);

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.

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

Capture return code of Perl system command with redirection

I am trying to capture the return code of a system call. To make it simple, consider a command that you know returns code 1, for example the following bash script t.sh:
#! /bin/bash
exit 1
Then consider:
use warnings;
use strict;
use feature qw(say);
my $res=system("t.sh");
say ($?>>8);
$res=system("t.sh &>/dev/null");
say ($?>>8);
which prints
1
0
Why does the second system call (the one with error redirection) give me a zero return code?
I can't duplicate your issue with Bash v4.1.2:
$ perl -wE 'system( q{/bin/bash -c "true &> /dev/null"} ); say $? >> 8'
0
$ perl -wE 'system( q{/bin/bash -c "false &> /dev/null"} ); say $? >> 8'
1
However, the &> redirection operator is not portable and should generally be avoided. I'm not sure, but it seems like it may not have worked properly in earlier releases.*
The following syntax is semantically equivalent and much more portable:
>file 2>&1
(note the & in 2>&1)
Use it instead.
* According to the Advanced Bash Scripting Guide, "This operator is now functional, as of Bash 4, final release."

How to check if a Perl script doesn't have any compilation errors?

I am calling many Perl scripts in my Bash script (sometimes from csh also).
At the start of the Bash script I want to put a test which checks if all the Perl scripts are devoid of any compilation errors.
One way of doing this would be to actually call the Perl script from the Bash script and grep for "compilation error" in the piped log file, but this becomes messy as different Perl scripts are called at different points in the code, so I want to do this at the very start of the Bash script.
Is there a way to check if the Perl script has no compilation error?
Beware!!
Using the below command to check compilation errors in your Perl program can be dangerous.
$ perl -c yourperlprogram
Randal has written a very nice article on this topic which you should check out
Sanity-checking your Perl code (Linux Magazine Column 91, Mar 2007)
Quoting from his article:
Probably the simplest thing we can tell is "is it valid?". For this,
we invoke perl itself, passing the compile-only switch:
perl -c ourprogram
For this operation, perl compiles the program,
but stops just short of the execution phase. This means that every
part of the program text is translated into the internal data
structure that represents the working program, but we haven't actually
executed any code. If there are any syntax errors, we're informed, and
the compilation aborts.
Actually, that's a bit of a lie. Thanks to BEGIN blocks (including
their layered-on cousin, the use directive), some Perl code may have
been executed during this theoretically safe "syntax check". For
example, if your code contains:
BEGIN { warn "Hello, world!\n" }
then you will see that message,
even during perl -c! This is somewhat surprising to people who
consider "compile only" to mean "executes no code". Consider the
code that contains:
BEGIN { system "rm", "-rf", "/" }
and you'll see the problem with
that argument. Oops.
Apart from perl -c program.pl, it's also better to find warnings using the command:
perl -w program.pl
For details see: http://www.perl.com/pub/2004/08/09/commandline.html
I use the following part of a bash func for larger perl projects :
# foreach perl app in the src/perl dir
while read -r dir ; do
echo -e "\n"
echo "start compiling $dir ..." ;
cd $product_instance_dir/src/perl/$dir ;
# run the autoloader utility
find . -name '*.pm' -exec perl -MAutoSplit -e 'autosplit($ARGV[0], $ARGV[1], 0, 1, 1)' {} \;
# foreach perl file check the syntax by setting the correct INC dirs
while read -r file ; do
perl -MCarp::Always -I `pwd` -I `pwd`/lib -wc "$file"
# run the perltidy inline
# perltidy -b "$file"
# sleep 3
ret=$? ;
test $ret -ne 0 && break 2 ;
done < <(find "." -type f \( -name "*.pl" -or -name "*.pm" \))
test $ret -ne 0 && break ;
echo "stop compiling $dir ..." ;
echo -e "\n\n"
cd $product_instance_dir ;
done < <(ls -1 "src/perl")
When you need to check errors/warnings before running but your file depends on mutliple other files you can add option -I:
perl -I /path/to/dependency/lib -c /path/to/file/to/check
Edit: from man perlrun
Directories specified by -I are prepended to the search path for modules (#INC).