Using bash -c and Globbing - bash

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

Related

Unable to execute ssh command containing parentheses with perl: "syntax error near unexpected token `('"

If I run this command from the command line, it works as expected on the remote server:
ssh admin#example.com "docker exec -it -d tasks_live_work_ec2_test_server /bin/sh -c \"/usr/bin/nvim -c 'silent! call SetupInstantServer()'\""
However, if I try to execute it from a perl script, I get errors:
my $cmd = qq|docker exec -it -d ${image_name}_server /bin/sh -c \"/usr/bin/nvim -c 'silent! call SetupInstantServer()'\"|;
`ssh admin\#example.com "$cmd"`;
bash: -c: line 1: syntax error near unexpected token '('`
Escaping the parens with backslashes suppresses the error, but the SetupInstantServer function in vim never gets called.
What I would do, using 2 here-doc:
#!/usr/bin/perl
system<<PerlEOF;
ssh admin\#example.com<<ShellEOF
docker exec -it -d ${image_name}_server /bin/sh -c "
/usr/bin/nvim -c 'silent! call SetupInstantServer()'
"
ShellEOF
PerlEOF
You can decide to add quotes on a 'HereDoc' to prevent shell expansion or the need to escape #. Up to your needs.
Check perldoc perlop#Quote-and-Quote-like-Operators

ssh: bash: line 5: syntax error near unexpected token `(' [duplicate]

for example:
ssh localhost echo *([^.])
requires me to pass -O extglob to the remote shell bash like so:
ssh localhost -O extglob echo *([^.])
however, ssh then thinks the -O is for itself, so I try again:
ssh localhost -- -O extglob echo *([^.])
but then bash thinks -- is for itself.
how can I pass -O extglob to bash through ssh?
thanks.
update: i would prefer not to ask ssh to ask bash to launch another bash:
ssh yourserver bash -O extglob -c "'echo *([^.])'"
Update: As mentioned in the comments the extglob will lead to a syntax error. I've managed it by piping the command to stdin:
ssh yourserver -- bash -O extglob <<'EOF'
echo *([^.])
EOF
I would start an additonal shell remotely. Like this:
ssh yourserver bash -O extglob -c 'ls -al'
Although it is not required in this example I would advice you to use -- after ssh arguments (as you mentioned):
ssh yourserver -- bash -O extglob -c 'ls -al'
This will prevent ssh from parsing the command to execute remotely as arguments.
But you can also pass the option to the shell ssh itself starts for you using the shopt bash builtin. Note that ; separates the two commands.
ssh yourserver -- 'shopt -s extglob; ls -al'

Bash negative wildcard using sub shell with `bash -c`

In bash, I can use a negative wildcard to glob all files in a directory that don't match some pattern, for example:
echo src/main/webapp/!(WEB-INF)
This works fine.
However, if I try to use exactly the same wildcard with bash -c to pass the command as an argument to a new bash shell, I get a syntax error:
$ bash -c 'echo src/main/webapp/!(WEB-INF)'
bash: -c: line 0: syntax error near unexpected token `('
bash: -c: line 0: `echo src/main/webapp/!(WEB-INF)'
Note that if I use a different glob, like bash -c 'echo src/main/webapp/*' it works as expected.
Why doesn't bash accept the same negative glob with -c as it does when run normally, and how can I get it to accept this negative glob?
That's because !(..) is a extended glob pattern that is turned on by default in your interactive bash shell, but in an explicit sub-shell launched with -c, the option is turned off. You can see that
$ shopt | grep extglob
extglob on
$ bash -c 'shopt | grep extglob'
extglob off
One way to turn on the option explicitly in command line would be to use the -O flag followed by the option to be turned on
$ bash -O extglob -c 'shopt | grep extglob'
extglob on
See extglob on Greg's Wiki for the list of extended glob patterns supported and The Shopt Builtin for a list of the extended shell options and which ones are enabled by default.
It happens the feature at stake is only enabled by default in an interactive shell. In bash, this is controlled by the extglob option:
extglob
If set, the extended pattern matching features described above (see Pattern Matching) are enabled.
To confirm this, you can run for example:
$ bash -c 'shopt -p | grep extglob'
shopt -u extglob
$ bash -i -c 'shopt -p | grep extglob'
shopt -s extglob

Proper syntax for bash exec

I am trying to do the following:
if ps aux | grep "[t]ransporter_pulldown.py" > /dev/null
then
echo "Script is already running. Skipping"
else
exec "sudo STAGE=production $DIR/transporter_pulldown.py" # this line errors
fi
$ sudo STAGE=production $DIR/transporter_pulldown.py works on the command line, but in a bash script it gives me:
./transporter_pulldown.sh: line 9:
exec: /Users/david/Desktop/Avails/scripts/STAGE=production
/Users/david/Desktop/Avails/scripts/transporter_pulldown.py:
cannot execute: No such file or directory
What would be the correct syntax here?
sudo isn't a command interpreter thus its trying to execute the first argument as a command.
Instead try this:
exec sudo bash -c "STAGE=production $DIR/transporter_pulldown.py"
This creates uses a new bash processes to interpret the variables and execute your python script. Also note that $DIR will be interpreted by the shell you're typing in rather than the shell that is being executed. To force it to be interpreted in the new bash process use single quotes.

How to specify zeroeth argument

I'm writing a bash script that starts the tcsh interpreter as a login shell and has it execute my_command. The tcsh man page says that there are two ways to start a login shell. The first is to use /bin/tcsh -l with no other arguments. Not an option, because I need the shell to execute my_command. The second is to specify a dash (-) as the zeroeth argument.
Now the bash exec command with the -l option does exactly this, and in fact the following works perfectly:
#!/bin/bash
exec -l /bin/tcsh -c my_command
Except... I can't use exec because I need the script to come back and do some other things afterwards! So how can I specify - as the zeroeth argument to /bin/tcsh without using exec?
You can enclose the exec command into a sub-shell of your script.
#!/bin/bash
(exec -l /bin/tcsh -c my_command)
# ... whatever else you need to do after the command is done
You can write a wrapper (w.sh) script that contains:
#!/bin/bash
exec -l /bin/tcsh -c my_command
and execute w.sh in your main script.

Resources