Issue
I hope you can help me with the following problem:
The Situation
- I need to find files in various folders and copy them to another folder. The files and folders can contain white spaces and umlauts.
- The filenames contain an ID and a string like: "2022-01-11-02 super important file"
- The filenames I need to find are collected in a textfile named ids.txt. This file only contains the IDs but not the whole filename as a string.
What I want to achieve:
- I want to read out ids.txt line by line.
- For every line in ids.txt I want to do a
find
search and copycp
the result to destination.
So far I tried:
for n in $(cat ids.txt); do find /home/alex/testzone/ -name "$n" -exec cp {} /home/alex/testzone/output \; ;
while read -r ids; do find /home/alex/testzone -name "$ids" -exec cp {} /home/alex/testzone/output \; ; done < ids.txt
The output folder remains empty. Not using -exec
also gives no (search)results.
I was thinking that -name "$ids"
is the root cause here. My files contain the ID + a String so I should search for names containing the ID plus a variable string (star)
- As argument for
-name
I also tried"$ids *"
"$ids"" *"
and so on with no luck.
Is there an argument that I can use in conjunction with find instead of using the star in the -name
argument?
Do you have any solution for me to automate this process in a bash script to read out ids.txt file, search the filenames and copy them over to specified folder?
In the end I would like to create a bash script that takes ids.txt and the search-folder and the output-folder as arguments like:
my-id-search.sh /home/alex/testzone/ids.txt /home/alex/testzone/ /home/alex/testzone/output
EDIT: This is some example content of the ids.txt file where only ids are listed (not the whole filename):
2022-01-11-01
2022-01-11-02
2020-12-01-62
EDIT II: Going on with the solution from tripleee:
#!/bin/bash
grep . $1 | while read -r id; do
echo "Der Suchbegriff lautet:"$id; echo;
find /home/alex/testzone -name "$id*" -exec cp {} /home/alex/testzone/ausgabe \;
done
In case my ids.txt file contains empty lines the -name "$id*"
will be -name *
which in turn finds all files and copies all files.
Trying to prevent empty line to be read does not seem to work. They should be filtered by the expression grep . $1 |
. What am I doing wrong?
Solution
If your destination folder is always the same, the quickest and absolutely most elegant solution is to run a single find
command to look for all of the files.
sed 's/.*/-o\n—name\n&*/' ids.txt |
xargs -I {} find -false {} -exec cp {} /home/alex/testzone/output +
The -false
predicate is a bit of a hack to allow the list of actual predicates to start with -o
(as in "or").
This could fail if ids.txt
is too large to fit into a single xargs
invocation, or if your sed
does not understand \n
to mean a literal newline.
(Here's a fix for the latter case:
xargs printf '-o\n-name\n%s*\n' <ids.txt |
...
Still the inherent problem with using xargs find
like this is that xargs
could split the list between -o
and -name
or between -name
and the actual file name pattern if it needs to run more than one find
command to process all the arguments.
A slightly hackish solution to that is to ensure that each pair is a single string, and then separately split them back out again:
xargs printf '-o_-name_%s*\n' <ids.txt |
xargs bash -c 'arr=("$@"); find -false ${arr[@]/-o_-name_/-o -name } -exec cp {} "$0"' /home/alex/testzone/ausgabe
where we temporarily hold the arguments in an array where each file name and its flags is a single item, and then replace the flags into separate tokens. This still won't work correctly if the file names you operate on contain literal shell metacharacters like *
etc.)
A more mundane solution fixes your while read
attempt by adding the missing wildcard in the -name
argument. (I also took the liberty to rename the variable, since read
will only read one argument at a time, so the variable name should be singular.)
while read -r id; do
find /home/alex/testzone -name "$id*" -exec cp {} /home/alex/testzone/output \;
done < ids.txt
Answered By - tripleee Answer Checked By - Cary Denson (WPSolving Admin)