"find" command with a variable directory - bash

I'm trying to list the files in a directory that is given by the variable DIR. My code looks like this so far:
for i in `find $DIR -name "*.txt"
The variable DIR is already defined. I'm not sure what the syntax is here.

ls "${DIR}/*.txt"
or
find "${DIR}" -name "*.txt"
should do the trick. The first one only lists *.txt files in the directory itself, the second one also *.txt files in subdirectories.

I guess you want to execute a given action on all files with extension "txt" under $DIR and/or its subdirs. As usual there are different solutions.
This one:
$ for i in $(find "$DIR" -name \*.txt) ; do echo "Do something with ${i}" ; done
won't work if file path (either the file itself or one subdirectory) contains spaces.
But you can use this:
$ find "$DIR" -type f -name \*.txt | while read i ; do echo "Do something with ${i}" ; done
or this:
$ find "$DIR" -type f -name \*.txt -print0 | xargs -0 -I {} echo "Do something with {}"
or this:
$ find "$DIR" -type f -name \*.txt -exec echo "Do something with {}" \;
or... 100 additional solutions.

Not sure what you want.
find $DIR -name "*.txt" -print
will list all the files that end with .txt and are located in $DIR or its subdirectories. You can omit the -print as that is the default behaviour anyway.
If you have a simple thing you want to do with this file, you can use find's -exec function:
find $DIR -name "*.txt" -exec wc -l {} \;
Or you can use a loop:
for f in `find $DIR -name "*.txt"`; do
wc -l $f
mv $f /some/other/dir/
fi
note: as #mauro helpfully pointed out, this will not work if the DIR or the file names contain spaces.
Cheers

Related

Change extension for every file in dir, except *.txt

I have a directory containing lot of files - txt and others.
I want to change extension those others file to txt
For now - i use this:
find . ! -name '*.txt' -type f -exec ls -f {} + > to_txt.txt
for i in ``cat to_txt.txt``; do
mv $i $i.txt && echo $i "File extension have been changed" || echo "Something went wrong"
done;
rm to_txt.txt
Script works fine, but i think it is clumsy
Is there any smarter and elegant way to do this?
Just use the -exec to perform the mv command:
find . ! -name '*.txt' -type f -exec mv {} {}.txt \;
# ^^^^^^^^^^^^
# here the magic
How does this work?
find . ! -name '*.txt' -type f is what you already had: it looks for those files whose name does not end with .txt.
Then, the key is the usage of -exec: there, we make use of {} that carries the value of every file that has been found. Since it acts as a variable, you can use it as so and perform commands. In this case, you want to do mv $file $file.txt, so this is what we do: mv {} {}.txt. To have it work, we finally have to add the \; part.

delete a file present in multiple directories based on the status of find command in unix

I need to delete a file present in multiple directories if it is found else ignore. I tried the following snippet.
ls $dir/"$input.xml" 2> /dev/null
var = `echo$?`
if [[ $var == 0 ]]; then
echo -e "\n Deleting...\n"
rm $dir/"$input.xml"
It failed.
Can anyone suggest me a better solution or modify the above snippet to suit the solution?
Not 100% sure what do you mean with "delete a file present in multiple directories if it is found else ignore". Assuming that you simply want to delete some files that are somewhere under $dir, do this:
Use find to find the files, and pipe to xargs rm:
find "$dir" -type f -name "*.xml" | xargs rm
If your filename is likely to contain spaces then do this:
find "$dir" -type f -name "*.xml" -print0 | xargs -0 rm
To supress the rm error message in case there are no files:
find "$dir" -type f -name "*.xml" -print0 | xargs -0 rm 2>/dev/null
To make your code working Try this [Insert space],
`echo $?`
Instead of this,
`echo$?`

Loop thru directory and return directory name

Im trying to loop thru a directory (non recursive) and I only want to list the directory name, not the path.
find /dir/* -type d -prune -exec basename {} \;
This returns a list of directories in the dir and it works.
folder 1
this is folder2
And I want to loop thru these so I did:
for i in $(/dir/* -type d -prune -exec basename {} \;)
do
echo ${i}
done
But the for loop loops thru each word and not row. which results in this:
folder
1
this
is
folder2
I know there is a lot of threads on this but I haven't found anyone that works for me. Especally with spaces in the name.
Does anyone know how to solve this?
If you want to loop through directory names then you can use;
( cd /dir && for f in */; do echo "$f"; done )
In case you want to loop thru the find results only then better way to do that is:
while read -r f; do
echo "$f"
done < <(find /dir/ -type d -prune -exec basename '{}' \;)
This is preferred since it avoids spawning a subshell (though find -exec will create subshells).
Use a while loop instead:
find /dir/* -type d -prune -exec basename {} \; | while IFS= read -r line
do
echo "$line"
done
find /dir -maxdepth 1 -type d -printf %f

removing files that have extension .bin then printing bye

filename=file.bin
extension=$(echo ${filename}|awk -F\. '{print $2}')
if [ ${extension} == "bin" ]; then
rm *.extenstion
fi
would something like this work how do I delete all files that have the same extention in a folder
You don't need to extract the extension yourself, this is what globbing is for. Simply do:
rm *.bin
Or recursively find ./ -name "*.bin" -exec rm -f {} \;
Aside from globbing, this is also doable with find.
find -type f -name "*.bin" -exec rm {} \;
Or more efficiently, with newer version of find:
find -type f -name "*.bin" -exec rm {} +
which is equivalent to
find -type f -name "*.bin" | xargs rm
Note: by default, find will do it recursively.

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