How to remove everything in a directory except one file in a subdirectory? - bash

I need to delete everything in directory d1, except the file d1/d2/f1.txt. How can I do that in bash?

This works. It will delete everything, but the directories in path of f1.txt and of course the file itself.
find d1/ ! -iregex '\(d1/\|d1/d2\|d1/d2/f1.txt\)' -delete
However, I would strongly suggest against using -delete as it is permanent and mistyping a character could be disastorous...
You should try something like this instead, putting files and directories in trash folder first just in case you delete a file you don't want to delete you can recover it.
mkdir -p ~/.Trash
find d1/ ! -iregex '\(d1/\|d1/d2\|d1/d2/f1.txt\)' -exec mv {} ~/.Trash \;

Find the contents to delete except for (!) specific file:
find d1/ -type f ! -name 'd1/d2/f1.txt' -delete

Related

removing directory and sub directory which is not present in the list

This is my directory structure
find -type f -print0 | xargs -0 ls -t
./lisst.txt ./SAMN03272855/SRR1734376/SRR1734376_1.fastq.gz
./SAMN03272854/SRR1734375/SRR1734375_2.fastq.gz ./SAMN07605670/SRR6006890/SRR6006890_2.fastq.gz
./SAMN03272854/SRR1734375/SRR1734375_1.fastq.gz ./SAMN07605670/SRR6006890/SRR6006890_1.fastq.gz
./SAMN03272855/SRR1734376/SRR1734376_2.fastq.gz
So this is a small subset of my folder/files where i have around 70.
I have a made a list of files which i want to keep and other i would like to delete.
My list.txt contains SAMN03272854,SAMN03272855 but I want to remove SAMN07605670.
I ran this
find . ! -name 'lisst.txt' -type d -exec rm -vrf {} +
It removed everything
QUESTION UPDATE
In my list it contains the folder i want to keep and the one which are not there are to be removed.
The folders which are to be removed also contains subdirectories and files. I want to remove everything
Your command selects each directory in the tree, except a directories of the funny name lisst.txt. Once it finds a directory, you do a recursive remove of this directory. No surprise that your files are gone.
You can't use rm -r when you want to spare certain files from deletion. This means that you also can't remove a directory, which somewhere below in its subtree has a file you want to keep.
I would run two find commands: The first removes all the files, ignoring directories, and second one removes all directories, which are empty (bottom-up). Assuming that SAMN03272854 is indeed a file (as you told us in your question), this would be:
find . -type f \( ! \( -name SAMN03272854 -o -name SAMN03272855 \) \) -exec rm {}
find . -depth -type d -exec rmdir {} 2>/dev/null
The error redirection in the latter command suppresses messages from rmdir for directories which still contain files you want to keep. Of course other messages are also suppressed. I would during debugging run the command without error redirection, to see whether it is basically correct.
Things would get more complicated, if you have files and directories to keep, because to keep a directory likely implies to keep all the files below it. In this case, you can use the -prune option of find, which excludes directories including their subdirectories from being processed. See the find man page, which gives examples for this.

Trying to find files containing an identifier, then move them to a new directory within terminal

I'm a beginner with this stuff and seem to be running into an issue.
Basically, I have many files with names containing a keyword (let's call it "Category1") within a directory. For example:
ABC-Category1-XYZ.txt
I'm trying to move them from a directory into another directory with the same name as the keyword.
I started with this:
find /path_A -name "*Category1*" -exec mv {} /path_A/Category1 \;
It spit out something like this:
mv: rename /path_A/Category1 to /path_A/Category1/Category1: Invalid
Argument
So I did some fiddling and hypothesized that the problem was caused by the command trying to move the directory Category1 into itself(maybe). I decided to exclude directories from the search so it would only attempt to move files. I came up with this:
find /path_A -name "*Category1*" \(! -type d \) -exec mv {} /path_A/Category1 \;
This did move the files from their original location to where I wanted them, but it still gave me something like:
mv: /path_A/Category1/ABC-Category1-XYZ.txt and
/path_A/Category1/ABC-Category1-XYZ.txt are identical
I'm no expert, so I could be wrong... but I believe the command is trying to find and move the files from their original directory, then find them again. The directory Category1 is a subdirectory of the starting point, /path_A, So i believe it is finding the files it just moved in the directory Category1 and attempting to move them again.
Can anyone help me fix this issue?
You are creating new files that find tries to process. Safest approach is to move them somewhere else not in the path_A you are searching with find.
Or you can use prune to ignore that directory if you don't have any other directory matching:
find /path_A -name '*Category1*' -prune -type f -exec mv {} /path_A/Category1/ \;
Although another post has been accepted, let me post a proper answer.
Would you please try:
find /path_A -name 'Category1' -prune -o -type f -name '*Category1*' -exec mv -- {} /path_A/Category1/ \;
The option -prune is rather a command than a condition. It tells find to
ignore the directory tree specified by the conditions before -prune.
In this case it excludes the directory Category1 from the search.
The following -o is logical OR and may be interpreted something like instead or else. The order of the options makes difference.
Please be noticed the 1st category1 is the directory name to exclude and the 2nd *Category1* is the filenames to find.
If you are not sure which files are the result of find, try to execute:
find /path_A -name 'Category1' -prune -o -type f -name '*Category1*' -print
then tweak the options to see the change of output.

Iteratively deleting files of a specific type

I have subfolders with .tmp files that I wish to delete, and trying to find a one-liner for doing so.
for item in folder1/*/*; do rm *.tmp; done
From this I get a bunch of messages saying:
rm: cannot remove ‘*.tmp’: No such file or directory
I get the same problem when I try doing other operations. What am I missing here?
P.S. I know I could recursively delete without a for loop with rm -rf *.tmp but I would like to know how to do it using an iterative loop, so that I can use it to perform other actions apart from rm by just replacing the "action" part of the statement.
Thanks in advance,
Jan
You can use:
find folder1 -mindepth 2 -maxdepth 2 -type f -name "*.tmp" -exec rm -f {} \;
This is going to find files (-type f) in the second directory level only (-mindept 2 -maxdepth 2) which name end with "tmp", and delete them.
You can try with "ls" instead of "rm" first, to check if the command is finding what you are looking for.

How to delete a file in any of the directories or subdirectories except one subdirectory

I want to delete a file from a directory which contains many subdirectories but the deletion should not happen in one subdiretory(searc) whose name is already predefined but path varies as shown below.So now how to delete a file i am using the below command
find . -type f -name "*.txt" -exec rm -f {} \;
this command deletes all the files in the directory.So How can we delete the file without serching that subdirectory.
The subdirectory file name will be same but the path will different
for eg
Main
|
a--> searc
|
b-->x--->searc
|
c-->y-->x-->searc
now the
the subdirectory not to be searched can be present any where as shown above
I think you want the -prune option. In combination with a successful name match, this prevents descent into the named directories. Example:
% mkdir -p test/{a,b,c}
% touch test/{a,b,c}/foo.txt
% find test -name b -prune -o -name '*.txt' -print
test/a/foo.txt
test/c/foo.txt
I am not completely sure what you're asking, so I can give only somewhat generic advice.
You already know the -name option. This refers to the filename only. You can, however, also use -wholename (a.k.a. -path), which refers to the full path (beginning with the one given as first option to find).
So if you want to delete all *.txt files except in the foo/bar subdirectory, you can do this:
find . -type f -name "*.txt" ! -wholename "./foo/bar/*" -delete
Note the -delete option; it doesn't require a subshell, and is easier to type.
If you would like to exclude a certain directory name regardless of where in the tree it might be, just don't "root" it. In the above example, foo/bar was "rooted" to ./, so only a top-level foo/bar would match. If you write ! -wholename "*/foo/bar/*" instead (allowing anything before or after via the *), you would exclude any files below any directory foo/bar from the operation.
You can use xargs instead of the exec
find .... <without the --exec stuff> | grep -v 'your search' | xargs echo rm -f
Try this first. If it is satisfactory, you can remove the echo.

Problems using find and cp to copy just .jpg files from a LOT of directories to one new path

I tried the search, but couldn't find the answer to my specific problem.
When I use,
find /recovered_files "*.jpg" -type f -exec cp {} /out \;
to copy all .jpg files from directories within the /recovered_files directory, the /out directory gets filled with every single file (jpg, txt, xml etc etc) from within the source directories.
Can anyone please explain wherein my stupidity lies, pleeeeeease???
Many thanks, Mark.
What you're doing at the moment is equivalent to calling cp /dir/dir/dir/file.jpg /out for each file, which will copy the file into /out. Thus, all of the files are being put into the same directory.
rsync allows filters to select only certain files to be copied. Change from and to to the appropriate directories in the following:
rsync -r from/* to --include=*.jpg --filter='-! */' --prune-empty-dirs
Credit to this post for this solution.
Edit: changed to rsync solution. Original as follows:
find from -name "*.jpg" -type f -exec mkdir -p to/$(dirname {}) \; -exec cp --parents {} to \;
You should replace from and to with the appropriate locations, and this form won't quite work if from begins with /. Just cd to / first if you need to. Also, you'll end up with all the files inside to underneath the entire directory structure of from, but you can just move them back out again.

Resources