Using find and exec inside while loop - bash

I've a .txt file which contains names of various files.
When I simply use a while loop it works fine,
while read -r name
do
echo "$name"
done <fileNames.txt
But,
when I try to use find inside the loop, like this:
while read -r name
do
find ./ -iname "$name" -exec sed -i '1s/^/NEW LINE INSERTED \n/' '{}' ';'
done < fileNames.txt
nothing happens!
If i use the find outside the loop like with a specific file name it does what it's supposed to do, I can also use it on all files with a specific file-type but it doesn't work inside the loop.
What am I doing wrong over here?
I'm trying to read file names from a file, search for it inside a folder recursively and then append a line in the beginning using sed.

Use xargs instead to capture the results of find
while read -r name
do
find ./ -iname "$name" |xargs sed -i '1s/^/NEW LINE INSERTED \n/'
done <fileNames.txt

Related

Append file name with source folder using the FIND command

I need to strip files out of a number of directories that all have the same file name a.txt. The difference comes from the parent folder so
example1\a.txt
example2\a.txt
...
so I am hoping to run a FIND command that will capture a.txt but not overwrite the file as it moves from folder to folder.
so the output would be
example1_a.txt
example2_a.txt
So from another post the FIND command I want is the following
find . -name "a.txt" -execdir echo cp -v {} /path/to/dest/ \;
So I want to modify in some way to append the source folder to the file. so my guess is to manipulate {} somehow to do it.
Thanks in advance
A one liner might be possible, but you could use this:
#!/bin/bash
targetprefix="targetdir"
find . -name "a.txt" -print0 | while read -r -d '' line
do
path=$(dirname "$line")
newpath=$(echo "${path#./}" | tr '/' '_')
target="$targetprefix/$newpath"
filename=$(basename "$line")
cp -v $line $target/$filename
done
change variable "targetprefix" to be the destination directory you desire.
this find with -print0 and while comes from https://mywiki.wooledge.org/BashFAQ/001
since results from find all start with "./", I use "${path#./}" to remove that prefix.
the tr replaces all subsequent "/" with an underscore. This will take care of sub directories
WARNING: I did not test all "weird" directory and filename formats for proper execution (like carriage returns in filenames!).

script read file contents and copy files

I wrote a script in bash that should read the contents of a text file, look for the corresponding files for each line, and copy them to another folder. It's not copying all the files, only two, the third and the last.
#!/bin/bash
filelist=~/Desktop/file.txt
sourcedir=~/ownCloud2
destdir=~/Desktop/file_out
while read line; do
find $sourcedir -name $line -exec cp '{}' $destdir/$line \;
echo find $sourcedir -name $line
sleep 1
done < "$filelist"
If I use this string on the command line it finds me and copies the file.
find ~/ownCloud2 -name 123456AA.pdf -exec cp '{}' ~/Desktop/file_out/123456AA.pdf \;
If I use the script instead it doesn't work.
I used your exact script and had no problems, for both bash or sh, so maybe you are using another shell in your shebang line.
Use find only when you need to find the file "somewhere" in multiple directories under the search start point.
If you know the exact directory in which the file is located, there is no need to use find. Just use the simple copy command.
Also, if you use "cp -v ..." instead of the "echo", you might see what the command is actually doing, from which you might spot what is wrong.

Shell script affects only on file instead of all of them

I am using a shell script to remove the XML tags of a set of files in a folder. This is how my file looks like:
#!/bin/sh
find texts -type f -name '*.xml' -exec sh -c '
mkdir -p modified
file="$0"
sed "s/<[^>]*>//g" "$file" > modified/modified_texts
' {} ';'
This is supposed to take all the files(using $file) in the "texts" folder, remove their XML tags and place the files without the XML tags into the file "modified".
The problem is that, instead of taking all the files, it is using just one, and filling the file "modified_texts" with the content of one of the files(without XML tags, that part works).
I don't really understand what I'm doing wrong, so I would appreciate any help.
Instead of doing the output redirection (with truncation!) for every sed command, move it to the outer scope, so the output file is opened (and its prior contents are truncated) only once, before find is started at all.
#!/bin/sh
mkdir -p modified # this only needs to happen once, so move it outside
find texts -type f -name '*.xml' -exec sed 's/<[^>]*>//g' {} ';' > modified/modified_texts

Select files reading names from a text file

I have a directory of files. I want to select some of them based on a text file I have. The text file has partial names of the files which I need to select.
I tried using find
ls | while read -r line; do find -type f -name $line; done < ../src_pdm3012/good_G20P1.txt
I also tried using grep for it does not seem to work.
Since names are partial, you must find files with a name that contains the line you read, not a name that equals it:
while read -r line; do find -type f -name "*$line*"; done < ../src_pdm3012/good_G20P1.txt
Since you wanted to use grep, here is what you were looking for:
ls -a | grep -F -f ../src_pdm3012/good_G20P1.txt
... but keep in mind it is not a good practice to grep ls output

Replace the complete filenames for files with their MD5 hash string of the content in bash

Problem:
I have a bunch of files in a folder,i want to rename all of them to the md5 of the content of the file.
What i tried:
This is the command i tried.
for i in $(find /home/admin/test -type f);do mv $i $(md5sum $i|cut -d" " -f 1);done
But this is failing after sometime with the error and only some files are getting renamed leaving rest untouched.
mv: missing destination file operand after /home/admin/test/help.txt
Try `mv --help' for more information.
Is the implementation correct? Am i doing something wrong in the script.
Make things simple by making use the glob patterns that the shell provides, instead of using external utilities like find. Also see Why you don't read lines with "for"
Navigate inside the folder /home/admin/test and do the following which should be sufficient
for file in *; do
[ -f "$file" ] || continue
md5sum -- "$file" | { read sum _; mv "$file" "$sum"; }
done
Try using echo inplace of mv first to check once if they files are renamed as expected.
To go to sub-directories below, which I assume would also be your requirement, enable globstar, which is one of the extended globing options provided by the shell to go deeper
shopt -s globstar
for file in **/*; do
If you want to recursively rename all files with their md5 hash, you could try this:
find /home/admin/test -type f -exec bash -c 'md5sum "$1" | while read s f; do mv "${f#*./}" "$(dirname ${f#*./})/$s"; done' _ {} \;
The hash and filename is given as argument into the s and f variables. The ${f#*./} removes the prefix added by md5sum and find commands.
Note that if some file have exact same content, it will end up with only 1 file.

Resources