Shell globbing negation doesn't work in bash - bash

I'm currently confused why shell globbing in terminal works with negation but shows an error when running in bash.
Take the commands executed in the terminal below, which shows all js files within the ./HTML directory except for js files that ends with .bundle.js.
$ shopt -s globstar
$ ls ./HTML/**/!(*.bundle).js
The command above works perfectly, now let's put it in a bash file
list-js.sh
#!/usr/bin/env bash
shopt -s globstar
ls ./HTML/**/!(*.bundle).js
Executing it in a terminal:
$ bash list-js.sh
list-js.sh: line 4: syntax error near unexpected token `('
list-js.sh: line 4: `ls ./HTML/**/!(*.bundle).js'
As you can see, it shows a syntax error.

globstar only enables the ** pattern. The extglob option allows !(...). Somewhere in your interactive shell, that has already been enabled (perhaps in your .bashrc, perhaps you typed shopt -s extglob earlier). However, it needs to be enabled explicitly in your script, since such settings are not inherited from the shell that starts the script.
#!/usr/bin/env bash
shopt -s globstar extglob
ls ./HTML/**/!(*.bundle).js
(As an aside, ** without globstar does not cause a syntax error because it is treated simply as two adjacent *s, the second one being redundant.)

Related

scp command fails with syntax error when used in a bin/sh script

The following command works perfectly when called directly from the terminal
scp -r ./!(node_modules|public) $SERVER:$WEBSITE_SRC
but when the same command is added to a shell script it fails with the following error
line 9: syntax error near unexpected token `('
here is the script for reference:
#!/bin/sh
set -e
SERVER=127.0.0.1
WEBSITE_SRC=~/website/src
echo "Deploying changes to the website src code"
scp ./.env* $SERVER:$WEBSITE_SRC
scp -r ./!(node_modules|public) $SERVER:$WEBSITE_SRC
!(node_modules|public) is extended globbing syntax. You should do two things.
Change the shebang line to #!/bin/bash to make sure your script is run by bash.
Put shopt -s extglob near the top to enable extended globbing syntax.
If it works interactively, it's probably because you have shopt -s extglob in one of your shell initialization files (e.g. ~/.bashrc).

Problem using cp command with negation (!) on crontab

#!/bin/bash
cd /var/www/html/tpd
cp -pr !(edc|dti|swb|audio|blog|buy|dpt|dpt.git|dpt-staging-server|dti_old|images|images2|images_linkshare|smarty-3.1.30|swb.com|swb.com.temp|talent|template_c|video|yout|yout-admin|All_About_Your_Canine_Friend.pdf|Canine_Cuisine.pdf|definitiveguide.pdf|GroomingYourDogAtHome-FreeReport.pdf|HowToStopYourPuppyOrOlderDogFromBiting.pdf|Network Merchants API.pdf|SuperDogsandPuppies.pdf|TopDogs.pdf|UltimateBreedGuide.pdf|10CommonProblemsofAdultDogs.pdf|10CommonProblemsofPuppies.pdf|45commonlyaskedquestionsondoggrooming.pdf|g.tar.gz|h.tar.gz|dti_back.tar.gz|Canine_Cuisine.zip|deluxe_version.zip|h.zip|StyleXPInstallMale.zip|UltimateBreedGuide.zip|videotranscripts.zip|101ways.zip) weeklyBackup
tar -cvf weeklyBackup.tar.gz weeklyBackup
rm -rf weeklyBackup
I am taking a weekly backup for only some selected files and this is the script that I am using. It works perfectly when running manually but when I am entering this is a crontab, it returns an error line 3: syntax error near unexpected token `('
The syntax !(name1|name2|name3) is what's called an "extglob". This is an optional extension to bash, not enabled by default.
To enable this syntax, you need to run (as a prior line in your script):
shopt -s extglob
Presumably your dotfiles are already doing this for interactive shells, which is why the syntax works for you out-of-the-box. (Also ensure that if your script is invoked from cron with sh scriptname, bash scriptname is used instead, or the code is modified to honor the shebang).

Why does pattern "*.so?(.*)" produce a syntax error in a script but not on command line?

The pattern is a little unusual because I added the trailing "?(.*)" portion. It works on command line as I expected but I get a syntax error for the same in a script.
$ bash --version
GNU bash, version 4.3.11(1)-release (i686-pc-linux-gnu)
...
$ cat x.sh
touch a.so a.so.1
ls *.so?(.*)
rm *.so?(.*)
$ touch a.so a.so.1
$ ls *.so?(.*)
a.so a.so.1
$ rm *.so?(.*)
$ ls
x.sh
$ bash x.sh
x.sh: line 2: syntax error near unexpected token `('
x.sh: line 2: `ls *.so?(.*)'
$
You are using an extended glob but these aren't enabled by default within a script. In order to using them, they must be explicitly enabled. You can do so by adding this before the line:
shopt -s extglob
To disable them later on in the script, you can use shopt -u extglob.
As chepner rightly points out, this feature isn't enabled by default in the interactive shell, either. Presumably, this line is present either in one of your system-wide startup scripts or one of your personal ones.

Regular expression on a remote script

I'm trying to run a script on a remote machine (both machines use bash), using ssh, which has the following lines:
cd /home/invitado/
rm -r !(Desktop|Downloads|Videos|Pictures)
So when I run ssh hostname './remove', I get this error:
syntax error near unexpected token `('
I tried appending this line to the script
shopt -s extglob
But I'm still having the same error, so what should I do? Thanks for your help.
shopt -s extglob should be before rm -r !(Desktop|Downloads|Videos|Pictures).
To verify, see what is the original value of extglob by running just shopt.
Note: In my case, that value was set when I run the command rm directly from the shell. But when I put it in script, it failed. Realized that extglob is set by default when running from interactive shell. & It is disabled by default when running inside the script.

Unable to enable globstar in Bash 4

I put the following unsuccessfully to my .bashrc
shopt -s globstar
I am trying to test the command in action by
ls **/*.c
and by comparing it to
ls */*/*.c
How can you enable globstar in Bash 4?
Hmm. shopt -s globstar should work.
To debug, make sure you are running Bash 4:
$SHELL --version
Then check the setting of globstar:
shopt globstar
If it is unset, try setting it manually:
shopt -s globstar
Now see if that works. If it does, you might want to look into why your .bashrc isn't working. Did you remember to restart you shell after editing your .bashrc, or load it with . .bashrc?

Resources