Search recursively for a list of filenames in shell - bash

I have a list of file names inside filenames.txt like the following:
1.sql
2.sql
3.sql
..
..
500.sql
I want to search for the file names in a directory and its sub-directories like the following:
Dir1/1.sql
Dir1/2.sql
Dir2/3.sql
Dir3/4.sql
Dir4/5.sql
Dir4/6.sql
..
..
etc
and copy the founded files to another directory.
I tried:
$ for i in `cat filenames.txt`; do `find ./* -type f -printf "%f\n"|grep -ie "$i" && cp -t "$i" /home/user/other_directory/"$i"`; done
but this doesn't work.

because the first argument of cp is "$i" (the matching pattern) and not the filename
find . -type f -iname "$i" -exec cp -t "{}" /home/user/other_directory \;

Related

use command find and copy but using a list.txt with names

I have a list.txt with different filenames and I want to find all those 3600 filename in subdirectories and then copy to /destination_folder.
Can I use the command find /path/ {file.txt} then copy to /destination_folder ?
The list.txt should have the following filenames/lines:
test_20180724004008_4270.txt.bz2
test_20180724020008_4278.txt.bz2
test_20180724034009_4288.txt.bz2
test_20180724060009_4302.txt.bz2
test_20180724061009_4303.txt.bz2
test_20180724062010_4304.txt.bz2
test_20180724063010_4305.txt.bz2
test_20180724065010_4307.txt.bz2
test_20180724070010_4308.txt.bz2
test_20180724071010_4309.txt.bz2
test_20180724072010_4310.txt.bz2
test_20180724072815_4311.txt.bz2
test_20180724073507_4312.txt.bz2
test_20180724074608_4314.txt.bz2
test_20180724075041_4315.txt.bz2
test_20180724075450_4316.txt.bz2
test_20180724075843_4317.txt.bz2
test_20180724075843_4317.txt.bz2
test_20180724080207_4318.txt.bz2
test_20180724080522_4319.txt.bz2
test_20180724080826_4320.txt.bz2
test_20180724081121_4321.txt.bz2
................................
You will probably want to make a list of all of the files in a directory, then use your list to iterate through the list of files found.
First save your list of files found to a file
find . -type f > foundFiles.txt
Then you need to use your file to search the other
cat list.txt | while read line
do
if [ `grep -c "${line}" foundFiles.txt` ]
then
cp -v $(grep "${line}" foundFiles.txt) /destination_folder/
fi
done
I'll let you take the base and make it into a script for use again.
You could use echo and sed:
echo $(sed "s/.*/\"\0\"/;s/^/ -name /;s/$/ -o/;$ s/-o//" list.txt)
This outputs a list of files to be used in find command:
-name "file1.txt.bz2" -o -name "file2.txt.bz2" -o -name "file3.txt.bz2"
Then use -exec cp -t targetDir {} + in find to copy the files:
find \( $(eval echo $(sed "s/.*/\"\0\"/;s/^/ -name /;s/$/ -o/;$ s/-o//" list.txt)) \) -exec cp -t targetDir {} +
Loop through the file and append the results to your destination folder:
for i in `cat list.txt`;
do cp `find * -name $i` destination_folder/;
done
This finds all the files in list.txt and copies those files to destination_folder/.
The for i in `cat list.txt` creates a variable i that loops through the entire file.
The cp `find * -name $i` destination_folder/ finds the path to file and copies it to the destination_folder/.

Rename files in several subdirectories

I want to rename a file present in several subdirectories using bash script.
my files are in folders:
./FolderA/ABCD/ABCD_Something.ctl
./FolderA/EFGH/EFGH_Something.ctl
./FolderA/WXYZ/WXYZ_Something.ctl
I want to rename all of the .ctl file with the same name (name.ctl).
I tried several command using mv or rename but didnt work.
Working from FolderA:
find . -name '*.ctl' -exec rename *.ctl name.ctl '{}' \;
or
for f in ./*/*.ctl; do mv "$f" "${f/*.ctl/name .ctl}"; done
or
for f in $(find . -type f -name '*.ctl'); do mv $f $(echo "$f" | sed 's/*.ctl/name.ctl/'); done
Can you help me using bash?
thanks
You can do this with one line with:
find . -name *.ctl -exec sh -c 'mv "$1" `dirname "$1"`/name.ctl' x {} \;
The x just allows the filename to be positional character 1 rather than 0 which (in my opinion) wrong to use as a parameter.
Try this:
find . -name '*.ctl' | while read f; do
dn=$(dirname "${f}")
# remove the echo after you sanity check the output
echo mv "${f}" "${dn}/name.ctl"
done
find should get all the files you want, dirname will get just the directory name, and mv will perform the rename. You can remove the quotes if you're sure that you'll never have spaces in the names.

Iterating over directory and replacing '+' from a file name to '_' in bash

I have a directory named as assets, which in further has a set of directories.
user_images
|-content_images
|-original
|-cropped
|-resize
|-gallery_images
|-slider_images
|-logo
These folders can have folders like original, cropped, resize. And these folders further will have images. These images are named something like this – 14562345+Image.jpeg. I need to replace all the images/files that have + to _.
for f in ls -a;
do
if [[ $f == ​+​ ]]
then
cp "$f" "${f//+/_}"
fi
done
I was able to do this in the current directory. But I need to iterate this to other many other directories. How can I do that?
You can use this loop using find in a process substitution:
cd user_images
while IFS= read -r -d '' f; do
echo "$f"
mv "$f" "${f//+/_}"
done < <(find . -name '*+*' -type f -print0)
With find -exec:
find user_images -type f \
-exec bash -c '[[ $0 == *+* ]] && mv "$0" "${0//+/_}"' {} \;
Notice that this uses mv and not cp as the question states "rename", but simply replace by cp if you want to keep the original files.
The bash -c is required to be able to manipulate the file names, otherwise we could use {} directly in the -exec action.
The following will run recursively and will rename all files replacing + with a _ :
find . -name '*+*' -type f -execdir bash -c 'for f; do mv "$f" "${f//+/_}"' _ {} +
Notice the use of -execdir :
Like -exec, but the specified command is run from the subdirectory containing the matched file, which is not normally the directory in which you started find -- Quoted from man find.
Which will protect us in case of directory names matching the pattern *+* which you do not want to rename.

Prepend text to beginning of file names

I would like to prepend a string of text to the beginning of each file in a directory. The string is uniwisc.
When I run the script:
#!/bin/sh
url="ftp://rammftp.cira.colostate.edu/Lindsey/spc/ir/"
wget -r -nd --no-parent -nc -P /awips2/edex/data/goes14/ $url
find /awips2/edex/data/goes14/ -type f -exec cp {} /awips2/edex/data/uniwisc/ \;
for f in /awips2/edex/data/uniwisc/*;
do
f="$(basename $f)"
mv "$f" "uniwisc.$f"
done;
find /awips2/edex/data/uniwisc/ -type f -mmin -6 -exec mv {} /awips2/edex/data/manual/ \;
exit 0
I get the error mv: cannot stat '<filenames>' "No such file or directory.
There are a number of different ways that you can do that.
Using paramater expansion, which is built into the bash shell:
for f in <dir path>/*; do
mv "$f" "${f%/*}/uniwisc.${f##*/}"
done
Using the rename command:
rename 's!^!uniwisc.!' *
Using the basename, as CodeGnome suggested:
for f in <dir path>/*; do
mv "$f" "$(dirname "$f")/uniwisc.$(basename "$f")"
done
I was going to write more methods, but there are a lot of them and they don't change significantly. Personally, I'd use the rename command in that situation.

using find with exec

I want to copy files found by find (with exec cp option) but, i'd like to change name of those files - e.g find ... -exec cp '{}' test_path/"test_"'{}' , which to my test_path should copy all files found by find but with prefix 'test'. but it ain't work.
I'd be glad if anyone could give me some ideas how to do it.
best regards
for i in `find . -name "FILES.EXT"`; do cp $i test_path/test_`basename $i`; done
It is assumed that you are in the directory that has the files to be copied and test_path is a subdir of it.
if you have Bash 4.0 and assuming you are find txt files
cd /path
for file in ./**/*.txt
do
echo cp "$file" "/test_path/test${file}"
done
of with GNU find
find /path -type f -iname "*.txt" | while read -r -d"" FILE
do
cp "$FILE" "test_${FILE}"
done
OR another version of GNU find+bash
find /path -type f -name "*txt" -printf "cp '%p' '/tmp/test_%f'\n" | bash
OR this ugly one if you don't have GNU find
$ find /path -name '*.txt' -type f -exec basename {} \; | xargs -I file echo cp /path/file /destination/test_file
You should put the entire test_path/"test_"'{}' in ""
Like:
find ... -exec cp "{}" "test_path/test_{}" \;
I would break it up a bit, like this;
for line in `find /tmp -type f`; do FULL=$line; name=`echo $line|rev|cut -d / -f -1|rev` ; echo cp $FULL "new/location/test_$name" ;done
Here's the output;
cp /tmp/gcc.version new/location/test_gcc.version
cp /tmp/gcc.version2 new/location/test_gcc.version2
Naturally remove the echo from the last part, so it's not just echo'ng what it woudl of done and running cp

Resources