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

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

Related

bad substitution bash equivalent in sh

I have this script in Bash :
for conffile in $(ls -1 ${KSCFGISOLINUXFILESLOCATION}); do
for variable in CFG_CRYPT CFG_DISK CFG_DNS CFG_DOMAIN CFG_GATEWAY CFG_HOSTNAME CFG_IFACE CFG_IP CFG_NETMASK CFG_USERKEY CFG_ACTION CFG_TITLE CFG_INITURL CFG_OSVERS CFG_ISOBASE CFG_ISOPATH CFG_ISOPATH_SIMPLE SHI_BOOT_URL DEBUG UUID LOGDIR WWWDIR OSINITDIR WKGDIR; do
SUBSTITUTIONCOUNT=$( grep -c "%%${variable}%%" ${conffile} | cut -f1 -d" " )
if [ ${SUBSTITUTIONCOUNT} -gt 0 ]; then
echo "Modifying ${variable} in ${SUBSTITUTIONCOUNT} places"
echo "Updating ${variable} in ${conffile} to ${-variable}"
sed -i "s^%%${variable}%%^${-variable}^g" ${conffile} 2>&1
fi
done
done
and when I ran it in a sh shell it throws the following error:
/tmp/wf_script-JI1La1/rhel84isoinstall.sh: 158: /tmp/wf_script-JI1La1/rhel84isoinstall.sh: Bad substitution
The problem is that I'm trying to use a bash command in a sh shell
What would be the equivalent of ${!variable} in sh ?
I tried ${-variable} but it was a bad command.
Sh and Dash don't support this feature.
Make sure the script always runs with bash by seeing the shebang correctly and avoiding sh myscript (see Why does my bash code fail when I run it with sh?)
Alternatively, rewrite it to sh using eval, but be careful about code injection:
#!/bin/sh
name=PATH
eval "echo \"\$$name\""

Watch with Process Substitution

I often run the command
squeue -u $USER | tee >(wc -l)
where squeue is a Slurm command to see how many jobs you are running. This gives me both the output from squeue and automatically tells how many lines are in it.
How can I watch this command?
watch -n.1 "squeue -u $USER | tee >(wc -l)" results in
Every 0.1s: squeue -u randoms | tee >(wc -l) Wed May 9 14:46:36 2018
sh: -c: line 0: syntax error near unexpected token `('
sh: -c: line 0: `squeue -u randoms | tee >(wc -l)'
From the watch man page:
Note that command is given to "sh -c" which means that you may need to use extra quoting to get the desired effect.
sh -c also does not support process substitution, the syntax you're using here as >().
Fortunately, that syntax isn't actually needed for what you're doing:
watch -n.1 'out=$(squeue -u "$USER"); echo "$out"; { echo "$out" | wc -l; }'
...or, if you really want to use your original code even at a heavy performance penalty (starting not just one but two new shells every tenth of a second -- first sh, and then bash):
bash_cmd() { squeue -u "$USER" | tee >(wc -l); } # create a function
export -f bash_cmd # export function to the environment
watch -n.1 'bash -c bash_cmd' # call function from bash started from sh started by watch

diff command not working inside bash shell script

I am trying to run
diff <(tar -tvf HIVE_CLIENT.tar.gz | sort) <(tar -tvf YARN_CLIENT.tar.gz | sort)
putting this command inside script, when I execute script it shows error
syntax error near unexpected token `(' "
But when I do not put inside script rather than run from shell directly it works.
Probably your script is run with /bin/sh and not with /bin/bash, but command substitution is a bash feature and not implemented in sh. So I suppose you are using bash as your shell which is why it is working from the command line.
Try adding this prefix to your script, and remove existing shebangs (like #!/bin/sh or similar):
#!/bin/bash
You should try following two actions:
Use #!/bin/bash as your shebang (First line of your script)
This may be needed based on your bash, use only if opetion 1 does not help. Use following commands to flip between posix mode which is needed for process substitution:
set +o posix
diff <(tar -tvf HIVE_CLIENT.tar.gz | sort) <(tar -tvf YARN_CLIENT.tar.gz | sort)
set -o posix
Example:
wc -l <(ls -lrt)
sh: syntax error near unexpected token `('
set +o posix
wc -l <(ls -lrt)
114 /dev/fd/00
set -o posix
wc -l <(ls -lrt)
sh: syntax error near unexpected token `('

Bash functions inside process substitution

Getting an error trying to call a function inside a process substitution.
Is there any way to do this?
#!/bin/bash
function testfunc
{
echo "bork"
}
diff <(testfunc) <(echo "bork")
The error is:
bork.sh: line 7: syntax error near unexpected token `('
bork.sh: line 7: `diff <(testfunc) <(echo "bork")'
--Update--
Problem was calling sh bork.sh, instead of bash ./bork.sh . Moral of the story make sure which shell you are executing with.
There's no problem here:
$ chmod +x test.sh
$ ./test.sh
Clear diff. No problem!
$ bash -x ./test.sh
+ diff /dev/fd/63 /dev/fd/62
++ testfunc
++ echo bork
++ echo bork
Proof that it worked
Troubleshooting:
Maybe you
run in a restricted shell
you don't have /dev/fd available/mounted correctly (due to somekind of secure chroot jail?)
The problem is probably that you're running the command with sh instead of bash.
$ cat > xx.sh
#!/bin/bash
function testfunc
{
echo "bork"
}
diff <(testfunc) <(echo "bork")
$ sh xx.sh
xx.sh: line 7: syntax error near unexpected token `('
xx.sh: line 7: `diff <(testfunc) <(echo "bork")'
$ bash xx.sh
$
The process substitution is not portable to the shell in /bin/sh. See the Bash manual on POSIX mode and bullet 28:
Process substitution is not available.
Tested on Mac OS X 10.10.5 (Yosemite).

How does /bin/bash -c differ from executing a command directly?

I'm curious why the commmand:
for f in `/bin/ls /mydir | sort | tail -n 10`; do echo $f; done;
Outputs the last ten files in /mydir, but
/bin/bash -c "for f in `/bin/ls /mydir | sort | tail -n 10`; do echo $f; done;"
Outputs "syntax error near unexpected token '[file in /mydir]'"
You are using double-quotes, so the parent shell is interpolating backticks and variables before passing the argument to /bin/bash.
Thus, your /bin/bash is receiving the following arguments:
-c "for f in x
y
z
...
; do echo ; done;"
which is a syntax error.
To avoid this, use single quotes to pass your argument:
/bin/bash -c 'for f in `/bin/ls /mydir | sort | tail -n 10`; do echo $f; done;'
Different newline handling in your subcommand output. For example on my machine, using /bin I get this output for your first example:
rmdir
sh
sleep
stty
sync
tcsh
test
unlink
wait4path
zsh
But with the latter:
/bin/bash: -c: line 1: syntax error near unexpected token `sh'
/bin/bash: -c: line 1: `sh'
Because the command substitution takes place in your first shell in both cases, your first one works (the newlines are stripped out when making the command line), but in the second case it doesn't - they remain in the string thanks to your "". Using echo rather than bash -c can showcase this:
$ echo "for f in `/bin/ls /bin | sort | tail -n 10`; do echo \$f; done"
for f in rmdir
sh
sleep
stty
sync
tcsh
test
unlink
wait4path
zsh; do echo $f; done
You can see from that what your bash -c is seeing and why it doesn't work - the sh comes before the do!
You can use single quotes instead, but that will cause the subcommand to run in your new subshell:
$ /bin/bash -c 'for f in `/bin/ls /bin | sort | tail -n 10`; do echo $f; done'
rmdir
sh
sleep
stty
sync
tcsh
test
unlink
wait4path
zsh
If that's not ok, you need to get rid of those newlines:
$ /bin/bash -c "for f in `/bin/ls /bin | sort | tail -n 10 | tr '\n' ' '`; do echo \$f; done"
rmdir
sh
sleep
stty
sync
tcsh
test
unlink
wait4path
zsh

Resources