perl execute a command where input comes from <(cmd2 input) - bash

I have a Perl script that I run in Linux 64bit and it looks like this:
my $ret = `/my/cmd option1 option2 <(/my/cmd2 input)`
This works in bash, but when I try to execute the same command as a backtick in the Perl script, it complains:
sh: -c: line 0: syntax error near unexpected token `('
Any ideas?

I guess your /bin/sh is not linked to bash, try:
my $ret = `bash -c '/my/cmd option1 option2 <(/my/cmd2 input)'`
You can check what /bin/sh is linked to with:
% ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Apr 5 07:03 /bin/sh -> dash

Related

Why bash redirect returns error ("Directory nonexistent") under nohup

#!/bin/bash
echo "hello" > /dev/udp/localhost/9082
The script executes normally under current user, however bash redirect returns an error when launched with nohup:
$ nohup ./test_redir.sh &
$ nohup: ignoring input and appending output to ‘nohup.out’
[1]+ Exit 2 nohup ./test_redir.sh
$ cat nohup.out
./test_redir.sh: 1: ./test_redir.sh: cannot create /dev/udp/localhost/9082: Directory nonexistent
What can be changed to enable bash redirects with nohup?
The error message seems to be from dash (which does not support /dev/udp/) rather than bash:
$ bash -c 'date > /not-a-dir/file'
bash: line 1: /not-a-dir/file: No such file or directory
$ /bin/sh -c 'date > /not-a-dir/file'
/bin/sh: 1: cannot create /not-a-dir/file: Directory nonexistent
$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 2020-11-11 11:33 /bin/sh -> dash
$
Double check if your script really starts with #!/bin/bash and has execution permission (chmod +x). Or try:
$ nohup bash /your/script.sh &

Cannot use process substitution during docker build because bash goes into posix mode

In a Dockerfile, I want to use process substitution:
RUN echo <(echo '$DATA:'"$DATA")
But docker build runs every RUN command with /bin/sh. Apparently being run as sh causes bash to switch to POSIX mode, which does not allow process substitution:
/bin/sh: -c: line 0: syntax error near unexpected token `('
I tried switching off POSIX mode:
RUN set +o posix && echo <(echo '$DATA:'"$DATA")
But it seems the syntax error happens even before the first command is run. Same if I replace && with ;.
Note that the command (even the one that I used as a simplified example here) contains both single and double quotes, so I can't simply prepend bash -c.
The used shell is actually a bash, but it is invoked as /bin/sh by docker:
Step 7 : RUN ls -l /bin/sh
---> Running in 93a9809e12a7
lrwxrwxrwx 1 root root 9 Dec 28 03:38 /bin/sh -> /bin/bash
If you are sure you have bash in your image being built, then you can change the shell invokation by using the SHELL command, which I described in another question.
You can use SHELL [ "/bin/bash", "-c" ]. Consider:
$ docker build --no-cache - < <(echo '
> FROM fedora
> RUN cat <(echo hello world)
> ')
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM fedora
---> ef49352c9c21
Step 2/2 : RUN cat <(echo hello world)
---> Running in 573730ced3a3
/bin/sh: -c: line 0: syntax error near unexpected token `('
/bin/sh: -c: line 0: `cat <(echo hello world)'
The command '/bin/sh -c cat <(echo hello world)' returned a non-zero code: 1
$ docker build --no-cache - < <(echo '
> FROM fedora
> SHELL ["/bin/bash", "-c"]
> RUN cat <(echo hello world)
> ')
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM fedora
---> ef49352c9c21
Step 2/3 : SHELL ["/bin/bash", "-c"]
---> Running in e78260e6de42
Removing intermediate container e78260e6de42
---> ff6ec782a9f6
Step 3/3 : RUN cat <(echo hello world)
---> Running in afbb42bba5b4
hello world
Removing intermediate container afbb42bba5b4
---> 25f756dcff9b
Successfully built 25f756dcff9b
Assuming your sh is not bash, you can't use process substitution in shell mode directly; you need to spawn a bash session (non-login, non-interactive here):
RUN [ "/bin/bash", "-c", "echo <(echo '$DATA:'\"$DATA\")" ]
Here i have used the json (aka exec) form to make sure the quotes are easily managed, here you just need to escape quotes around $DATA: \"$DATA\" -- to prevent json interpretation beforehand.
If your sh is in fact bash, this should do:
RUN "echo <(echo '$DATA:'"$DATA")"
Also this just outputs the file descriptor, i am not rally sure about your plan.

difference between sh and bash when symlink is used

I have a shell script which uses process substitution
The script is:
#!/bin/bash
while read line
do
echo "$line"
done < <( grep "^abcd$" file.txt )
When I run the script using sh file.sh I get the following output
$sh file.sh
file.sh: line 5: syntax error near unexpected token `<'
file.sh: line 5: `done < <( grep "^abcd$" file.txt )'
When I run the script using bash file.sh, the script works.
Interestingly, sh is a soft-link mapped to /bin/bash.
$ which bash
/bin/bash
$ which sh
/usr/bin/sh
$ ls -l /usr/bin/sh
lrwxrwxrwx 1 root root 9 Jul 23 2012 /usr/bin/sh -> /bin/bash
$ ls -l /bin/bash
-rwxr-xr-x 1 root root 648016 Jul 12 2012 /bin/bash
I tested to make sure symbolic links are being followed in my shell using the following:
$ ./a.out
hello world
$ ln -s a.out a.link
$ ./a.link
hello world
$ ls -l a.out
-rwx--x--x 1 xxxx xxxx 16614 Dec 27 19:53 a.out
$ ls -l a.link
lrwxrwxrwx 1 xxxx xxxx 5 May 14 14:12 a.link -> a.out
I am unable to understand why sh file.sh does not execute as /bin/bash file.sh since sh is a symbolic link to /bin/bash.
Any insights will be much appreciated. Thanks.
When invoked as sh, bash enters posix
mode after the startup files are read. Process substitution is not recognized in posix mode. According to posix, <(foo) should direct input from the file named (foo). (Well, that is, according to my reading of the standard. The grammar is ambiguous in many places.)
EDIT: From the bash manual:
The following list is what’s changed when ‘POSIX mode’ is in effect:
...
Process substitution is not available.

Using bash -c and Globbing

I'm running gnu-parallel on a command that works fine when run from a bash shell but returns an error when parallel executes it with bash using the -c flag. I assume this has to do with the special globbing expression I'm using.
ls !(*site*).mol2
This returns successfully.
With the flag enabled the command fails
/bin/bash -c 'ls !(*site*).mol2'
/bin/bash: -c: line 0: syntax error near unexpected token `('
The manual only specifies that -c calls for bash to read the arguments for a string, am I missing something?
Edit:
I should add I need this to run from a gnu-parallel string, so the end resultant command must be runnable by /bin/bash -c "Some Command"
You should try the following code :
bash <<EOF
shopt -s extglob
ls !(*site*).mol2
EOF
Explanation :
when you run bash -c, you create a subshell, and shopt settings are not inherited.
EDIT
If you really need a one liner :
bash -O extglob -c 'ls !(*site*).mol2'
See this thread

Code does not work with sh -c, but works on sh directly

$ sh
sh-3.2$ if
> ps -ef | grep apple ;
> then
> echo APPLE
> fi ;
lazer 7584 7571 0 04:36 pts/4 00:00:00 grep apple
APPLE
sh-3.2$ exit
exit
$ which sh
/bin/sh
$ /bin/sh -c if ps -ef | grep apple ; then echo APPLE fi ;
bash: syntax error near unexpected token `then'
$
As above, my simple if statement works as expected when executed line by line but gives me the following error when executed using sh -c:
bash: syntax error near unexpected token `then'
What am I missing here?
Your interactive shell will be escaping the invocation via sh -c. In particular it's taking everyting after the semi-colon as a new statement.
Quote everything that you're feeding to /bin/sh e.g.
$ /bin/sh -c "if ps -ef | grep apple ; then echo APPLE fi ;"
I think you may also need to delimit further using semi-colons given that you're condensing everything onto one line, and would perhaps suggest you could use a heredoc.

Resources