Use "rm" command with inverse match - bash

I want to remove all files that do not match R1.fastq.gz in my list of files.
How do I use rm with inverse match?

Use the extended pattern syntax available in bash:
shopt -s extglob
printf '%s\n' !(R1.fastq.gz) # To verify the list of files the pattern matches
rm !(R1.fastq.gz) # To actually remove them.
Or, use find:
find . ! -name R1.fastq.gz -print # Verify
find . ! -name R1.fastq.gz -exec rm {} + # Delete
If your version of find supports it, you can use -delete instead of -exec rm {} +:
find . ! -name R1.fastq.gz -delete

You may want the "invert match" option, grep –v, which means "select lines which do not match the regex." and then remove them.
Something like rm $(ls | grep -v -e "R1.fastq.gz") should do it.
Please, note that this will erase all files in the folder you are on, except R1.fastq.gz

Related

How do I get bash's "find -rename" command to print what it's going to do without doing it?

When I do a mass rename operation, I will first do
for i in $(ls); do
echo $i $i.new_extension
done
and then I will check everything makes sense and replace echo with mv or cp and re-run the command.
How do I do the same with find -rename?
Use find with echo, then remove the echo, for example:
find . -name 'foo*' -exec echo "mv {} {}.bak" \;
# if OK, repeat without the echo:
find . -name 'foo*' -exec mv {} {}.bak \;
Alternatively, use find with rename --dry-run, then remove the --dry-run:
find . -name 'foo*' -exec rename --dry-run 's/foo/bar/' {} \;
# If OK, repeat without --dry-run:
find . -name 'foo*' -exec rename 's/foo/bar/' {} \;
If you can use a glob instead of find, use just rename --dry-run alone, then remove the --dry-run:
rename --dry-run 's/foo/bar/' foo*
# If OK, repeat without --dry-run:
rename 's/foo/bar/' foo*
Note that rename can be installed easily, for example with conda:
conda install rename
SEE ALSO:
rename manual (very helpful):
rename --man
For example:
-n, --dry-run, --just-print
Show how the files would be renamed, but don't actually do anything.

Rename all files in directory and subdirectory

How do I rename files in directory and subdirectory?
I found this program, but I need to go change files in subdirectory.
for file in *#me01
do
mv "$file" "${file/#me01/_me01}"
done
n#me01
to
n_me01
The following one-liner will likely work for you:
find . -type f -name '*#me01' -execdir rename '#me01' '_me01' {} \;
The following form is likely more correct as it will change only the last # to _ if there are multiple occurrences of #me01 in the file:
for f0 in $(find . -type f -name '*#me01')
do
f1=$(printf '%s' "$f0" | sed 's/#me01$/_me01/')
mv "$f0" "$f1"
done
This latter form is also more flexible and can be built upon more easily as the regex language in sed is much more powerful than rename expressions.
If rename of directories is also required the following can easily be added...
Either:
find . -type d -name '*#me01' -execdir rename '#me01' '_me01' {} \;
Or:
for d0 in $(find . -type d -name '*#me01')
do
d1=$(printf '%s' "$d0" | sed 's/#me01$/_me01/')
mv "$d0" "$d1"
done
Using bash:
shopt -s globstar
for name in **/*#me01; do
mv "$name" "${name%#me01}_me01"
done
This enables the globstar shell option in bash which makes ** match across path separators in pathnames.
It also uses a standard parameter substitution to delete the #me01 portion at the very end of the found pathname and replace it with _me01.

Delete all files and directories but certain ones using Bash

I'm writing a script that needs to erase everything from a directory except two directories, mysql and temp.
I've tried this:
ls * | grep -v mysql | grep -v temp | xargs rm -rf
but this also keeps all the files that have mysql in their name, that i don't need. it also doesn't delete any other directories.
any ideas?
You may try:
rm -rf !(mysql|init)
Which is POSIX defined:
Glob patterns can also contain pattern lists. A pattern list is a sequence
of one or more patterns separated by either | or &. ... The following list
describes valid sub-patterns.
...
!(pattern-list):
Matches any string that does not match the specified pattern-list.
...
Note: Please, take time to test it first! Either create some test folder, or simply echo the parameter substitution, as duly noted by #mnagel:
echo !(mysql|init)
Adding useful information: if the matching is not active, you may to enable/disable it by using:
shopt extglob # shows extglob status
shopt -s extglob # enables extglob
shopt -u extglob # disables extglob
This is usually a job for find. Try the following command (add -rf if you need a recursive delete):
find . -maxdepth 1 \! \( -name mysql -o -name temp \) -exec rm '{}' \;
(That is, find entries in . but not subdirectories that are not [named mysql or named tmp] and call rm on them.)
You can use find, ignore mysql and temp, and then rm -rf them.
find . ! -iname mysql ! -iname temp -exec rm -rf {} \;

Exclude a string from wildcard search in a shell

I am trying to exclude a certain string from a file search.
Suppose I have a list of files: file_Michael.txt, file_Thomas.txt, file_Anne.txt.
I want to be able and write something like
ls *<and not Thomas>.txt
to give me file_Michael.txt and file_Anne.txt, but not file_Thomas.txt.
The reverse is easy:
ls *Thomas.txt
Doing it with a single character is also easy:
ls *[^s].txt
But how to do it with a string?
Sebastian
You can use find to do this:
$ find . -name '*.txt' -a ! -name '*Thomas.txt'
With Bash
shopt -s extglob
ls !(*Thomas).txt
where the first line means "set extended globbing", see the manual for more information.
Some other ways could be:
find . -type f \( -iname "*.txt" -a -not -iname "*thomas*" \)
ls *txt |grep -vi "thomas"
If you are looping a wildcard, just skip the rest of the iteration if there is something you want to exclude.
for file in *.txt; do
case $file in *Thomas*) continue;; esac
: ... do stuff with "$file"
done

shell script to remove selected directories

i have bunch of dirs , say **a, b, c0, d, Z , foo, ** and so on.
I want to remove all the directories except dirs foo, foo2, a and b
can anyone provide me the syntax to do this shell?
Thanks
UPDATE
I just want to say Thank you to all of you for your responses!
echo `ls -1 -d */ | egrep -v '^(foo|foo2|a|b)/$'`
If you are satisfied with the output, replace echo with rmdir (or rm -r, if the directories still contain data).
Probably the easiest way;
mkdir ../tempdir
mv foo foo2 a b ../tempdir
rm *
mv ../tempdir/* .
rmdir ../tempdir
Please note that this deletes also all files, not just directories.
You can use find on a complicated command line, but perhaps the simplest and, more importantly, safest way is to create a file that lists all of the directories you want to remove. Then use the file as input to rm like this:
find . -maxdepth 1 -type d > dirs_to_remove
Now edit the file and take out any directories you want to keep, then use rm:
rm -ir $(<edited_dirs_to_remove)
Note the -i argument. It's optional and forces rm to ask you before deleting each file.
Also note the $(<filename) syntax, which is specific to bash and is equivalent to, but cheaper than $(cat filename).
One of the more powerful ways to do this sort of trick is using find + grep + xargs:
DONT_REMOVE='a|b|c0|d|Z|foo'
find . -type d -print | egrep -v "^\.$DONT_REMOVE\$" | xargs rm -r
The only trick here is making sure the pattern matches only those you don't want to remove.
The above pattern only matches files in the current directory. You can make it more or less
permissive, e.g:
IF_PATH_IS_IMMEDIATE_SUBDIR="^\./($DONT_REMOVE)$"
IF_PATH_ENDS_IN="/($DONT_REMOVE)$"
IF_PATH_CONTAINS="/($DONT_REMOVE)(/.*)?$"
Then pass one of these in your egrep, e.g:
find . -type d -print | egrep -v "$IF_PATH_ENDS_IN" | xargs rm -r
To invert the choice (ie. delete all those items) just remove the -v from the egrep
one way
find . -maxdepth 1 -type d \( ! -name "bar" -a ! -name "foo" -a ! -name "a" -a ! -name "b" \) -delete # to remove files as well, remove -type d
OR try using extglob
shopt -s extglob
rm -rf !(foo|bar|a|b)/ # to delete files as well, remove the last "/"
And yes, this assume you don't want the rest of the directories in your directory except the 4 directories you want.

Resources