Parentheses don't work in Bash script - bash

I'm writing a script that needs to erase everything from a directory except two directories, mysql and temp.
I asked a question earlier and got this code that works in the command line:
rm -rf !(mysql|temp)
However it doesn't work in the script. I get this error "Syntax error: "(" unexpected".
Is there something special about parentheses I need to do?

You probably need to explicitly enable extended patterns in your script:
shopt -s extglob

Related

failed to delete multiple folders inside one folder except 3 folders using rm

I am trying to delete all folders inside a folder except 2 3 using rm. But, command doesnt work with below error
Tried using escape character, but it will not delete folders.
Any solutions?
EDIT 1
Using double quotes after parentheses is a not working
EDIT 2
using shopt is also not working
Note 1: Shell Option needs to be set first for extglob. I had the same issue running my script until I added it to my .sh file. Link below is a reference from Unix stack exchange.
https://unix.stackexchange.com/questions/153862/remove-all-files-directories-except-for-one-file
Full Working Script: test.sh
shopt -s extglob;
cd Test;
rm -rfi !("atest3"|"atest2")
Note 2: If working in Ansible, the default shell is SH vs BASH. Due to this, you may need to call the script with BASH or use solutions found in this answer to get it working. The script below works in BASH. In ZSH it may require a setopt command similar to extglob.
Script call from SH basic terminal using bash
The following command will remove (rm) by force (f) recursively (r) and interactively (i) all files or folders except (! bang operator) for the file or folders strings ('arg1'|'arg2'|...) listed as piped (|) arguments.
If a nested files and folders is passed as an arg, but the parent is not, that file or folder will be deleted:
rm -rfi !('atest2'|'atest3')
Example of using rm with -rf flags and bang operator to prevent passed string arguments being deleted
I am leaving the below commands as examples from an earlier answer I wrote.
This command will create multiple folders:
mkdir {test1,atest2,atest3,atest4}
And this command will remove the folders:
rm -rfi {test1,atest2,atest3,atest4}
Example of mkdir with args and rm -rfi with args shell command

How to continue with nested for loops with a "zsh: no matches found" error? [duplicate]

I'm currently working on a script that deletes all the PNG files from my Desktop. I want to create an array of file paths then use the rm command on each one.
This is the relevant bit of code:
#!/usr/bin/env bash
shopt -s nullglob
files=("$HOME"/Desktop/*.png)
files_found="${#files[#]}"
shopt -u nullglob
It has been recommend that I use shopt in case of no matching files.
However I'm on MacOS and just discovered that shopt is not available for ZSH. When I run the script I get command not found: shopt.
I've found the ZSH has an equivalent called setopt however after reading through the documentation I can't quite figure out which option is the correct one to use in the case. I can't seem to find any examples either.
Can anyone point me in the right direction?
The corresponding option in zsh is CSH_NULL_GLOB (documented in man zshoptions).b
setopt CSH_NULL_GLOB
(As far as I can tell, the idea of a pattern disappearing rather than being treated literally comes from csh.)
The more zsh-like approach is not to set this as a general option (as suggested in the answer given by chepner), but to decide on each pattern, whether or you want to have the nullglob effect. For example,
for f in x*y*(N)
do
echo $f
done
simply skips the loop if there are no files matching the pattern.
Just come to the realisation that the issue of shopt not being found was due to me auto-loading the file as a ZSH function.
The script worked perfectly when I ran it like so:
bash ./tidy-desktop
Previously I had been running it just with the command tidy-desktop
Instead I now have this in my zsh_aliases:
tidy-desktop="~/.zshfn/tidy-desktop"
Thanks to #Charles Duffy for helping me figure out what was going on there!

Bash wildcard working in command line but not script

I've a basic ls command I'm running in my command line $ ls [root]/*, from which I can see the contents of my $root directory. But, when I run the same command in a script, I see this error: ls: [root]/*: No such file or directory. I'm going to paste the script below, but is someone able to tell why the command is running fine in the command line but not the script? Thank you.
#! /bin/bash
root="[root]"
ls "$root/*"
edit:
I tracked down the problem. The wildcard should not be inside the double quotes unless I'm looking for a file or directory with that name. The script below runs successfully.
#! /bin/bash
root="[root]"
ls "$root/"*
The best answer to my question was the edit I made to my question. I'll say it again.
I wanted to use an ls wildcard in a bash script and wrote this file:
#! /bin/bash
root="[root]"
ls "$root/*"
But, writing my ls this way looks for files or directories with the name *. In order to make use of the wildcard I need to leave * outside of the quotes. See the example below:
#! /bin/bash
root="[root]"
ls "$root/"*
note:
I'm using the square brackets [] in my question and answer contextually. The name of my directory isn't the literal string [root], it's something else, but the square brackets let the reader understand what's being said contextually.
But maybe a fake root path or name is better in the future.
There's a few issues here.
[root] is a shell globbing pattern that matches any of the three characters r, o or t. If you do ls [root] and you happen to have a directory or file called r, o or t, then that pattern would expand to the name of that file or directory.
To safely list the content of a directory whose real name is [root], you would need to quote the name, which brings us to the next point...
A shell globbing pattern does not expand in quotes. This means that "[root]/*" refers to something called *, literally, in the directory [root].
Your script would have to look like
#!/bin/sh
root='[root]'
ls "$root"/*
The existent answers do not solve the problem if you use complex patterns like this:
cp release/artifacts/${OS}/binary?(.exe) $DEST
Because it failed with:
line 6: syntax error near unexpected token `('
To solve that you should add the following option to the script:
#!/usr/bin/bash
shopt -s extglob

error of bash string concatenation

For me bash script is always some kind of tricky. I have
web_dir=/tng4/users/ldiao/AQF/wrf-chem-result/
rm "$web_dir""three_days_ago/*.gif"
the error message is
rm: cannot remove '/tng4/users/ldiao/AQF/wrf-chem-result/three_days_ago/*.gif':
No such file or directory
But if I change to
rm "$web_dir"three_days_ago/*.gif""
then it works. There is no spell errors. Can someone explains for me the reason? thanks!
The reason is that bash globbing does not work inside " "
Correct variant would be
rm -- "$web_dir/three_days_ago/"*.gif
Also, use -i option when playing with rm. This way if you make a mistake or a typo it wont delete all of your files unless you confirm that.

Filename substitution gives syntax error in script , works in console

I have several files under /var/log/uwsgi/ named similar to domain.123456789
I'm trying to loop over the files as given below
for FILE in /var/log/uwsgi/domain.+([[:digit:]]); do
gzip $FILE;
done
this works in console. but when run as a part of a script, i get the following syntax error.
script.sh: line 16: syntax error near unexpected token `('
how can i use substitution in shell scripts?
Make sure that the extglob shell option is enabled in your script by adding:
shopt -s extglob
Without this, the shell won't recognise your +([[:digit:]]) pattern and you will get an error.
You probably already have this set in your bash profile which is why it works in the console.

Resources