AlexOnLinux
AlexOnLinux

Reputation: 105

How to read out a file line by line and for every line do a search with find and copy the search result to destination?

I hope you can help me with the following problem:

The Situation

What I want to achieve:

So far I tried:

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)

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?

Upvotes: 0

Views: 1152

Answers (2)

tripleee
tripleee

Reputation: 189417

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

Upvotes: 2

user18098820
user18098820

Reputation:

Please try the following bash script copier.sh

#!/bin/bash
IFS=$'\n'        # make newlines the only separator
set -f           # disable globbing
file="files.txt" # name of file containing filenames
finish="finish"  # destination directory
while read -r n ; do (
        du -a | awk '{for(i=2;i<=NF;++i)printf $i" " ; print " "}' | grep $n | sed 's/ *$//g' | xargs -I '{}' cp '{}' $finish
  );

done < $file

which copies recursively all the files named in files.txt from . and it's subfiles to ./finish This new version works even if there are spaces in the directory names or file names.

Upvotes: 0

Related Questions