Escaping glob brackets in bash script [duplicate] - linux

Running this script, bash ./cleanup.bash,
#!/bin/bash
## Going to directory-moving stuff
rm -rf !(composer.json|.git)
Gives the error:
cleanup.bash: line 10: syntax error near unexpected token '('
cleanup.bash: line 10: 'rm -rf !(composer.json|.git)'
But if I run in in the terminal directly, there aren't any problems:
rm -rf !(composer.json|.git)
I tried stripping out all other lines, but I still get the error.
How do I enter this correctly in the Bash script?
I'm on Ubuntu, and this was all done locally, not on a remote.

I guess your problem is due to the shell extended glob option not set when run from the script. When you claim it works in the command line, you have somehow set the extglob flag which allow to !() globs.
Since the Bash script, whenever started with a #!/bin/bash, starts a new sub-shell, the extended options set in the parent shell may not be reflected in the new shell. To make it take effect, set it in the script after the shebang:
#!/bin/bash
shopt -s extglob
## Going to directory-moving stuff
rm -rf !(composer.json|.git)

Related

Linux Script is failing [duplicate]

#!/bin/bash
jobname="job_201312161447_0003"
jobname_pre=${jobname:0:16}
jobname_post=${jobname:17}
This bash script gives me Bad substitution error on ubuntu. Any help will be highly appreciated.
The default shell (/bin/sh) under Ubuntu points to dash, not bash.
me#pc:~$ readlink -f $(which sh)
/bin/dash
So if you chmod +x your_script_file.sh and then run it with ./your_script_file.sh, or if you run it with bash your_script_file.sh, it should work fine.
Running it with sh your_script_file.sh will not work because the hashbang line will be ignored and the script will be interpreted by dash, which does not support that string substitution syntax.
I had the same problem. Make sure your script didnt have
#!/bin/sh
at the top of your script. Instead, you should add
#!/bin/bash
For others that arrive here, this exact message will also appear when using the env variable syntax for commands, for example ${which sh} instead of the correct $(which sh)
Your script syntax is valid bash and good.
Possible causes for the failure:
Your bash is not really bash but ksh or some other shell which doesn't understand bash's parameter substitution. Because your script looks fine and works with bash.
Do ls -l /bin/bash and check it's really bash and not sym-linked to some other shell.
If you do have bash on your system, then you may be executing your script the wrong way like: ksh script.sh or sh script.sh (and your default shell is not bash). Since you have proper shebang, if you have bash ./script.sh or bash ./script.sh should be fine.
Try running the script explicitly using bash command rather than just executing it as executable.
Also, make sure you don't have an empty string for the first line of your script.
i.e. make sure #!/bin/bash is the very first line of your script.
Not relevant to your example, but you can also get the Bad substitution error in Bash for any substitution syntax that Bash does not recognize. This could be:
Stray whitespace. E.g. bash -c '${x }'
A typo. E.g. bash -c '${x;-}'
A feature that was added in a later Bash version. E.g. bash -c '${x#Q}' before Bash 4.4.
If you have multiple substitutions in the same expression, Bash may not be very helpful in pinpointing the problematic expression. E.g.:
$ bash -c '"${x } multiline string
$y"'
bash: line 1: ${x } multiline string
$y: bad substitution
Both - bash or dash - work, but the syntax needs to be:
FILENAME=/my/complex/path/name.ext
NEWNAME=${FILENAME%ext}new
I was adding a dollar sign twice in an expression with curly braces in bash:
cp -r $PROJECT_NAME ${$PROJECT_NAME}2
instead of
cp -r $PROJECT_NAME ${PROJECT_NAME}2
I have found that this issue is either caused by the marked answer or you have a line or space before the bash declaration
Looks like "+x" causes problems:
root#raspi1:~# cat > /tmp/btest
#!/bin/bash
jobname="job_201312161447_0003"
jobname_pre=${jobname:0:16}
jobname_post=${jobname:17}
root#raspi1:~# chmod +x /tmp/btest
root#raspi1:~# /tmp/btest
root#raspi1:~# sh -x /tmp/btest
+ jobname=job_201312161447_0003
/tmp/btest: 4: /tmp/btest: Bad substitution
in my case (under ubuntu 18.04), I have mixed $( ${} ) that works fine:
BACKUPED_NB=$(ls ${HOST_BACKUP_DIR}*${CONTAINER_NAME}.backup.sql.gz | wc --lines)
full example here.
I used #!bin/bash as well tried all approaches like no line before or after #!bin/bash.
Then also tried using +x but still didn't work.
Finally i tried running the script ./script.sh it worked fine.
#!/bin/bash
jobname="job_201312161447_0003"
jobname_post=${jobname:17}
root#ip-10-2-250-36:/home/bitnami/python-module/workflow_scripts# sh jaru.sh
jaru.sh: 3: jaru.sh: Bad substitution
root#ip-10-2-250-36:/home/bitnami/python-module/workflow_scripts# ./jaru.sh
root#ip-10-2-250-36:/home/bitnami/python-module/workflow_scripts#

Why isn't pattern matching working with 'rm' from bash script? [duplicate]

Running this script, bash ./cleanup.bash,
#!/bin/bash
## Going to directory-moving stuff
rm -rf !(composer.json|.git)
Gives the error:
cleanup.bash: line 10: syntax error near unexpected token '('
cleanup.bash: line 10: 'rm -rf !(composer.json|.git)'
But if I run in in the terminal directly, there aren't any problems:
rm -rf !(composer.json|.git)
I tried stripping out all other lines, but I still get the error.
How do I enter this correctly in the Bash script?
I'm on Ubuntu, and this was all done locally, not on a remote.
I guess your problem is due to the shell extended glob option not set when run from the script. When you claim it works in the command line, you have somehow set the extglob flag which allow to !() globs.
Since the Bash script, whenever started with a #!/bin/bash, starts a new sub-shell, the extended options set in the parent shell may not be reflected in the new shell. To make it take effect, set it in the script after the shebang:
#!/bin/bash
shopt -s extglob
## Going to directory-moving stuff
rm -rf !(composer.json|.git)

removing files except some in a bash script

I was trying to delete some files in a bash script and exclude some I still need afterwards. This is what I came up with:
if [ $var1 -eq 0 ]; then
echo "$var1 = 0"
shopt -s extglob
rm /home/someone/!(file1.txt|file2.png|file3.pdf|file4.csv)
else
echo "$var1 = 1"
shopt -s extglob
rm -rf /home/someone/!(file1.txt|file2.png|file3.pdf|file4.csv)
fi
It is working if I execute it manually on the shell, but in the script it does not even call the if-loop. As soon as I comment out the lines containing the "!" it works.
You cannot turn the extglob shell option from within a block or a function. E.g., this will fail:
shopt -u extglob
f() {
shopt -s extglob
echo *+(a)*
}
f
The reason is that the command shopt -s extglob is not executed when the block is parsed… and the parser will hence complain when it encounters the extended glob +(a). See the relevant section of the glob page on Greg's wiki:
extglob changes the way certain characters are parsed. It is necessary to have a newline (not just a semicolon) between shopt -s extglob and any subsequent commands to use it. You cannot enable extended globs inside a group command that uses them, because the entire block is parsed before the shopt is evaluated. Note that the typical function body is a group command. An unpleasant workaround could be to use a subshell command list as the function body.
If you really want this behavior, though, you can use eval, but that's really not recommended at all! Instead, move the shopt -s extglob out of the block. In fact, it is customary to put the shell options at the beginning of the script and use them throughout the script (when applicable). I don't think you'll run into any problems if you use extglob throughout the script.
As #choroba is hinting towards, you need to run your script either as bash my_script.sh or ./my_script.sh to run it with Bash. If you run sh my_script.sh the shell may not support extglob.
A useful way to tell what's actually happening is to add set -o xtrace to the top of the script (after the shebang line). That should tell you what actually gets executed. I'd also add set -o errexit to make sure the script stops at the first failing command, and quote the reference to $var1.

return no errors on command line syntax

If you run rm -r /var/some/dir/* and the dir is empty an error is returned
How can you supress this error so its not returned?
I use it in a linux bash script
Apart from redirecting to stderr, you can also use -f:
rm -rf /var/some/dir/*
From man rm:
-f, --force
ignore nonexistent files and arguments, never prompt
Redirect stderr:
rm -r /var/some/dir/* 2>/dev/null
Since you question mentions [if] dir is empty an error is returned (so assuming the directory exists), that's because the glob /var/some/dir/* matches nothing, hence bash (with the default unset nullglob and failglob) will pass the string /var/some/dir/* verbatim. And there are no such files, so rm complains.
Since you're using this in a bash script, I would advise you to proceed as follows:
shopt -s nullglob
shopt -u failglob
declare -a files=( /var/some/dir/* )
if (( ${#files[#]} )); then
rm -r -- "${files[#]}" # || error handling
fi
The shopt -s nullglob will make globs expand to nothing if no matching files exist,
You put the matching files in an array files (and because nullglob is set, the array files is empty if there are no matches),
If there are matching files, then rm them.
Proceeding like this will make your script more robust, as you'll be able to catch a genuine error occuring in rm (with the || error handling part) and not confuse this with an error occurring from a non-matched glob.
Doing so, you will not launch a random command with uncontrolled arguments.
Remark. It is not quite true that the error you obtain occurs only when the directory is empty. It occurs when the directory contains no files, except possibly hidden files. If you want to really purge the directory from also potential hidden files, you must shopt -s dotglob so that the glob will also match hidden files.

Executing shell script in ubuntu

Maybe I am missing something here but I have the following small bash script to delete some old files that get created when using flex and yacc its pretty simple but when I run the script it echos the result to the terminal but does not delete the file I'm probably missing something stupid could you guys point me in the right direction.
#!/bin/bash
echo "rm -f y.tab.h"; (just using one file for now)
I tried changing it to
echo rm -f y.tab.h;
but still no luck
I tried executing it with bash delete.sh and sh delete.sh and even using chmod +x on the file and executing it with ./
Remove echo statement
Just use
rm -f y.tab.h

Resources