unable to process files of a directory in for loop in linux - linux

i have directory that has 2 sub-directories and that again has few sub-directory and they have some files. I need to rename all the files to append an html extension to the filenames.
the directory structure looks like this
main-directory
sub-directory
sub-directory
sub-directory
file1
file2
and so on to lot of files
now i could not use something like this
for file in main-directory/*
do
if [ -f "$file" ]
then `mv "$file" "$file.html"`
fi
done
because the for loop wont use the path recursively. so i used something like this
for file in `ls -1R main-directory` // -1 for showing file and directory names separated by new lines and -R for recursive travel
do
if [ -f "$file" ]
then `mv "$file" "$file.html"`
fi
done
the above code is not able to rename files. to check whether the line
for file in `ls -1R main-directory`
is working i wrote something like this
for file in `ls -1R main-directory`
do
if [ -f "$file" ]
echo $file
done
this doesn't show anything. what can be wrong?

you can use find and look into of type file and then -exec to change all the file and then appending the .html.
find main-directory -type f -exec mv -v '{}' '{}'.html \;

In your first for loop, the mv command should not be in back-ticks.
In your second for loop, the if-statement has incorrect syntax. There is no then or fi. It should be:
for file in `ls -1R main-directory`
do
if [ -f "$file" ]
then
echo $file
fi
done
But even then, this won't work because ls -1R main-directory gives you just the file names, not the absolute paths to the file. Move your echo outside the if-statement to test:
for file in `ls -1R main-directory`
do
echo $file
done
Therefor ls -1R main-directory is not a good way to get all files in the current directory. Use find . -type f instead.

For some reason, I can never remember the find ... -exec syntax off the top of my head with the {} and the \;. Instead, I've fallen into the habit of just using a loop fed from find:
find main-directory -type f | while read file
do
mv "$file" "$file.html"
done
find outputs each file to stdout and the read file will consume one line at a time and set the contents of that line to the $file environment variable. You can then use that anywhere in the body of your loop.
I use this approach to solve lots of little problems like this where I need to loop over a bunch of output and do something useful. Because it is more versatile, I use it more than the esoteric find ... -exec {} \; approach.
Another trick is to prepend you command with echo to do a quick sanity check before doing potentially damaging things to your system:
find find main-directory -type f | while read file
do
echo mv "$file" "$file.html"
done

here is the answer to my question. people responded by 1 liners which are a neat approach but i didnt get much out of those 1 liners so here is something that i wanted
IFS=$'\n' // this is for setting field separator to new line because the default is whitespace
dir=main-directory
for file in `ls -1R main-directory | sed 's/:$//'` // -1 for showing file and directory names separated by new lines and -R for recursive travel
do
if [ -f "$dir/$file" ]
then `mv "$dir/$file" "$dir/$file.html"`
elif [ -d "$file" ]
then dir=$file
fi
done
here the sed 's/:$//' detects a : at the end of line and removes it. this was one of the things that prevented my code to run because whenever ls -1R main-directory detected a directory it appended a : at the end

Related

How to change extension of certain files? [duplicate]

I want to write a code is shell scripting which will rename all the files of extension .txt in a current directory to extension .c .Suppose my current directory contains some 100 .txt file. This number is not fixed.
for f in *.txt; do echo mv "$f" "${f%.txt}.c"; done
Remove "echo" when you're satisfied it's working. See the bash manual for the meaning of "%" here.
See man rename. You can rename multiple files providing regexp substitution.
rename 's/\.txt$/.c/' *.txt
If you don't have rename in you system, you can use find:
find . -name '*.txt' | while read FILE; do echo mv "$FILE" "$(echo "$FILE" | sed 's/\.txt$/.c/g')"; done
Remove echo when you verify it does what you want.
awk can do this trick too:
kent$ ls *.txt|awk '{o=$0;gsub(/txt$/,"c"); print "mv "o" "$0;}'|sh

Searching through every file in a directory (and in any sub-directories) one by one

I'm trying to loop through every file in a directory (including files in its subdirectories) and perform some action if the file meets an if-condition.
Part of my code is as follows:
for f in $direc/*
do
if grep -q 'search_term' $f; then
#action on this file
fi
done
However, this fails in the case of subdirectories. I would be very grateful if someone could help me out.
Thank you!
The -R option to grep will read all files in the directory tree including subdirectories. Combined with the -l option to print only the matching file names, you can use that to perform an action on each file that matches.
egrep -Rl pattern directory | while read path; do echo $path && mv $path /tmp; done
For example, that would print the file name and move the file to a different directory.
Find | xargs is the usual pattern I use, and has the advantage of not getting hung up on special characters in file names (spaces etc.) if you use the -print0 option of find.
find . -type f -print0 | xargs -0 -I{} sh -c "if grep -q 'search string' '{}'; then cmd-to-run '{}'; fi"
Yes because with this syntax, grep expect to process file(s) not directories. Minimal change to your script would be to test if $f is a file or not:
...
if [ -f "$f" ] && grep -q 'search_term' $f; then
...
In reality you would probably want to get list of files with patter match and act on those:
while read f; do
: #action on file file $f
done < <(grep -rl 'search_term' $direc/)
I've opted for getting the get the list of files through <(list) because piping it into while would cause the inside of your loop to run in another process (which could be a problem in particular if you expect any variable (changes) to be accessible from outside. And unlike simple for with `` it's not as as sensitive to what filenames you encounter (namely I have spaces in mind, this would still get confused by newlines though). Speaking of which:
while read -d "" f; do
: #action on file file $f
done < <(grep -rZl 'search_term' $direc/)
Nothing should be able to confuse that, as entries are nul character delimited and that one just must not appear in a file name.
Assuming no newlines in your file names:
find "$direc" -type f -exec grep -q 'search_term' {} \; -print |
while IFS= read -r f; do
#action on this file
done

Please tell me in detail about the following Script

I want to delete email "sudar.dudi#in.xyz.com" from all the files which lies in a particular directory. I used the below command after searching a lot.
The problem with this command is, it keeps on searching other than the desired directories and due to this command my other files got corrupted.
Please help me to correct this:
rrcg0/utioi
(in utioi direcotry are my files and the files are with specific names
like mpp.properties.JKH,mpp.properties.KJL,mpp.properties.KHF
likewise)
yes n|for file in $(find . -type f)
do
sed 's/sudar.dudi#in.xyz.com//g' $file > $file.tmp
mv $file.tmp $file
done
You do not need find when you know the directory.
When you want to check that an entry given by ls is a file, you can use a test:
mydir=rrcg0/utioi
for file in ${mydir}/*
do
if [ -f "${file}" ]; then
sed 's/sudar.dudi#in.xyz.com//g' $file > $file.tmp && mv $file.tmp $file
fi
done
When you only want properies files you can use
for file in ${mydir}/*properties*
I have used &&. This makes sure the mv command will only be done when the redirection succeeded.
I did not use the sed -i option, that option is not always supported.
EDIT: What about spaces in the filename?
When you might have spaces in the filename, the above script must be corrected:
sed 's/sudar.dudi#in.xyz.com//g' "$file" > "$file.tmp" && mv "$file.tmp" "$file"
Make it simpler, try using exec option of find and GNU sed option for inline editing
find <absolute dir path> -name "*" -type f -exec sed -i '/hello/d' {} \;
You can make changes to the existing files also, as then there is no need to move the file. Include -i in sed command.
Modifying codes of Walter A
mydir=rrcg0/utioi
for file in ${mydir}/*
do
if [ -f "${file}" ]; then
sed -i 's/sudar.dudi#in.xyz.com//g' $file
fi
done

getting filenames from directory in shell script

I would like to iterate a loop over all the file present in a directory using shell script. Further, I would like to display the contents from each file. I am passing directory as a command line argument.
I have a simple loop as follows:
for file in $1
do
cat $file
done
If I run
sh script.sh test
where test is a directory, I get content of first file only.
Could anyone please help me in this?
couple of alternatives:
compact modification of SMA's code:
for file in $1/*
do
[[ -f $file ]] && cat $file
done
or use find:
find $1 -type f -exec cat \{\} \;
Try something like:
for file in $1/*
do
if [[ -f $file ]] ##you could add -r to check if you have read permission for file or not
then
cat $file
fi
done

Shell Script for renaming and relocating the files

I am working on something and need to solve the following. I am giving a analogous version of mine problem.
Say we have a music directory, in which there are 200 directories corresponding to different movies. In each movie directory there are some music files.
Now, say a file music.mp3 is in folder movie.mp3 . I want to make a shell script such that it renames the file to movie_music.mp3 and put it in some folder that I mention to it. Basically, all the files in the subdirectories are to be renamed and to be put in a new directory.
Any workaround for this?
This script receives two arguments: the source folder and the destination folder. It will move every file under any directory under the source directory to the new directory with the new filename:
#!/bin.sh
echo "Moving from $1 to $2"
for dir in "$1"/*; do
if [ -d "$dir" ]; then
for file in "$dir"/*; do
if [ -f "$file" ]; then
echo "${file} -> $2/`basename "$dir"`_`basename "${file}"`"
mv "${file}" "$2"/`basename "$dir"`_`basename "${file}"`
fi
done
fi
done
Here is a sample:
bash move.sh dir dir2
Moving from dir to dir2
dir/d1/f1 -> dir2/d1_f1
dir/d1/f2 -> dir2/d1_f2
dir/d2/f1 -> dir2/d2_f1
dir/d2/f2 -> dir2/d2_f2
Bash:
newdir=path/to/new_directory;
find . -type d |while read d; do
find "$d" -type f -maxdepth 1 |while read f; do
movie="$(basename "$d" |sed 's/\(\..*\)\?//')"
mv "$f" "$newdir/$movie_$(basename $f)";
done;
done
Assuming the following directory tree:
./movie1:
movie1.mp3
./movie2:
movie2.mp3
The following one-liner will create 'mv' commands you can use:
find ./ | grep "movie.*/" | awk '{print "mv "$1" "$1}' | sed 's/\(.*\)\//\1_/'
EDIT:
If your directory structure contains only the relevant directories, you can expand use the following grep instead:
grep "\/.*\/.*"
Notice it looks file anything with at least one directory and one file. If you have multiple inner directories, it won't be good enough.

Resources