bash, delete all files with a pattern name - bash

I need to delete all files with a pattern name:  2020*.js
Inside a specific directory: server/db/migrations/
And then show what it have been deleted: `| xargs``
I'm trying this:
find . -name 'server/db/migrations/2020*.js' #-delete | xargs
But nothing is deleted, and shows nothing.
What I'm doing wrong?

The immediate problem is that -name only looks at the last component of the file name (so 2020xxx.js) and cannot match anything with a slash in it. You can use the -path predicate but the correct solution is to simply delete these files directly:
rm -v server/db/migrations/2020*.js
The find command is useful when you need to traverse subdirectories.
Also, piping the output from find to xargs does not do anything useful; if find prints the names by itself, xargs does not add any value, and if it doesn't, well, xargs can't do anything with an empty input.
If indeed you want to traverse subdirectories, try
find server/db/migrations/ -type f -name '2020*.js' -print -delete
If your shell supports ** you could equally use
rm -v server/db/migrations/**/2020*.js
which however has a robustness problem if there can be very many matching files (you get "command line too long"). In that scenario, probably fall back to find after all.

You're looking for something like this:
find server/db/migrations -type f -name '2020*.js' -delete -print

You have try this:
find . -name 'server/db/migrations/2020*.js' | xargs rm

Related

Use Find and xargs to delete dups in arraylist

I have arraylist of files and I am trying to use rm with xargs to remove files like:
dups=["test.csv","man.csv","teams.csv"]
How can I pass the complete dups array to find and delete these files?
I want to make changes below to make it work
find ${dups[#]} -type f -print0 | xargs -0 rm
Your find command is wrong.
# XXX buggy: read below
find foo bar baz -type f -print0
means look in the paths foo, bar, and baz, and print any actual files within those. (If one of the paths is a directory, it will find all files within that directory. If one of the paths is a file in the current directory, it will certainly find it, but then what do you need find for?)
If these are files in the current directory, simply
rm -- "${dups[#]}"
(notice also how to properly quote the array expansion).
If you want to look in all subdirectories for files with these names, you will need something like
find . -type f \( -name "test.csv" -o -name "man.csv" -o -name "teams.csv" \) -delete
or perhaps
find . -type f -regextype egrep -regex '.*/(test\.csv|man\.csv|teams\.csv)' -delete
though the -regex features are somewhat platform-dependent (try find -E instead of find -regextype egrep on *BSD/MacOS to enable ERE regex support).
Notice also how find has a built-in predicate -delete so you don't need the external utility rm at all. (Though if you wanted to run a different utility, find -exec utility {} + is still more efficient than xargs. Some really old find implementations didn't have the + syntax for -exec but you seem to be on Linux where it is widely supported.)
Building this command line from an array is not entirely trivial; I have proposed a duplicate which has a solution to a similar problem. But of course, if you are building the command from Java, it should be easy to figure out how to do this on the Java side instead of passing in an array to Bash; and then, you don't need Bash at all (you can pass this to find directly, or at least use sh instead of bash because the command doesn't require any Bash features).
I'm not a Java person, but from Python this would look like
import subprocess
command = ["find", ".", "-type", "f"]
prefix = "("
for filename in dups:
command.extend([prefix, "-name", filename])
prefix = "-o"
command.extend([")", "-delete"])
subprocess.run(command, check=True, encoding="utf-8")
Notice how the backslashes and quotes are not necessary when there is no shell involved.

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.

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.

using find with rsync is failing in a way I can't understand

I am trying to synchronise two directories, omitting certain files.
find . -type f -print0 |rsync -vupt -0 --files-from=- . /tmp/test
This copies every file of course, but works as expected
The problem arises when I try to limit the files copied. So if I want to copy all but the files ending ".part" :
find . -type f -\! -iname "*.part" -print0 |rsync -vupt -0 --files-from=- . /tmp/test
But this fails to copy anything at all.
If I remove the pipe I can see that find is outputting a stream that looks just like the kind of output from the first command but minus the file names I don't want to copy.
(I've tries -not and -name alternatives too without luck)
What am I doing wrong?
Thanks for any help.
The find negation is not a command, but an operator. You can use ! -iname "*.part" to negate that single expression.

How to return the absolute path of recursively matched arguments? (BASH)

OK, so simple enough.. I want to recursively search a directory for files with a specific extension - and then perform an action on those files.
# pwdENTER
/dir
# ls -R | grep .txt | xargs -I {} open {} ENTER
The file /dir/reallyinsubfolder.txt does not exist. ⬅ fails (bad)
Not output, but succeeds.. /dir/fileinthisfolder.txt ⬅ opens silently (good)
This does find ALL the files I am interested in… but only OPEN's those which happen to be "1-level" deep. In this case, the attempt to open /dir/reallyinsubfolder.txt fails, as reallyinsubfolder.txt is actually /dir/sub/reallyinsubfolder.txt.
I understand that grep is simply returning the matched filename… which then chokes (in this case), the open command, as it fails to reach down to the correct sub-directory to execute the file..
How do I get grep to return the full path of a match?
How about using the find command -
find /path/to/dir -type f -iname "*.txt" -exec action to perform {} \;
find . -name *.txt -exec open {};
(Decorate with backslashes of your needing)
I believe you're asking the wrong question; parsing ls(1) output in this fashion is far more trouble than it is worth.
What would work far more reliably:
find /dir -name '*.txt' -print0 | xargs -0 open
or
find /dir -name '*.txt' -exec open {} \;
find(1) does not mangle names nearly as much as ls(1) and makes executing programs on matched files far more reliable.

Resources