Please tell me in detail about the following Script - linux

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

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

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

unable to process files of a directory in for loop in 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

Add file extension to files with bash

What is the good way to add file extension ".jpg" to extension-less files with bash?
# Strip .jpg from all filenames
for f in *.jpg; do mv "$f" "${f%.jpg}"; done
# Add .jpg to all filenames (even those with .jpg already)
for f in *; do mv "$f" "$f.jpg"; done
# Add .jpg to all filenames...unless they are already .jpg
for f in *; do case "$f" in *.jpg) echo skipped $f;; *) mv "$f" "$f".jpg; esac; done
# Add .jpg to all filenames...unless they already have a . extension
for f in *; do case "$f" in *.*) echo skipped $f;; *) mv "$f" "$f".jpg; esac; done
You can use rename:
rename 's/(.*)/$1.jpg/' *
Another way - without loops
find . -type f -not -name "*.*" -print0 |\
xargs -0 file |\
grep 'JPEG image data' |\
sed 's/:.*//' |\
xargs -I % echo mv % %.jpg
Breakdown:
find all files without extension
check the file type
filter out only JPG files
delete filetype info
xargs run the "mv" for each file
the above command is for dry run, after it you should remove the "echo" before mv
EDIT
Some people suggesting that here is needed "Wrap path arguments in quotes; avoids argument splitting on paths with spaces".
Usually, this recommendation is true, in this case isn't. Because, here the % is got replaced not by shell expansion but by the xargs internally (directly), so the % will be substituted correctly even with spaces in filenames.
Simple demo:
$ mkdir xargstest
$ cd xargstest
# create two files with spaces in names
$ touch 'a b' 'c d'
$ find . -type f -print
./c d
./a b
# notice, here are spaces in the above paths
#the actual xargs mv WITHOUT quotes
$ find . -type f -print | xargs -I % mv % %.ext
$ find . -type f -print
./a b.ext
./c d.ext
# the result is correct even in case with spaces in the filenames...
Simple,
cd to the directory where your files are and:
for f in *;do mv $f $f.jpg;done
dry run:
rename -n s/$/.jpg/ *
actual renaming:
rename s/$/.jpg/ *
find . | while read FILE; do if [ $(file --mime-type -b "$FILE") == "image/jpeg" ]; then mv "$FILE" "$FILE".jpg; fi; done;
In my case i was not aware of the filetype so i used the mv command with the help of the file command to examine and possibly find the file type. This solution might not be perfect for all files since the file command might not recognize the filetype but it worked mostly good for me.
for f in *; do ext=$(file $f | awk '{print $2;}'); mv -n "$f" "$f.$ext"; done
The use of awk is to strip the second word of the string returned from the command file that is actually the extension.
rename --dry-run * -a ".jpg" # test
* -a ".jpg" # rename
You can use move multiple files. I am a maintainer of this project. The syntax is simple.
mmf files*
It will open your $EDITOR with all files names, or vim by default and you can simply highlight the end of all file names using Ctrl+v+G in vim , save the file,quit and that it , all your files are renamed
Ryan Li
The correct syntax for adding a file extension to multiple files within a directory which do not have a file extension is
find . | while read FILE; do if [[ -n `file --mime-type "$FILE" | grep 'message/rfc822'` ]]; then mv "$FILE" "$FILE".eml; fi; done;

How do I rename all folders and files to lowercase on Linux?

I have to rename a complete folder tree recursively so that no uppercase letter appears anywhere (it's C++ source code, but that shouldn't matter).
Bonus points for ignoring CVS and Subversion version control files/folders. The preferred way would be a shell script, since a shell should be available on any Linux box.
There were some valid arguments about details of the file renaming.
I think files with the same lowercase names should be overwritten; it's the user's problem. When checked out on a case-ignoring file system, it would overwrite the first one with the latter, too.
I would consider A-Z characters and transform them to a-z, everything else is just calling for problems (at least with source code).
The script would be needed to run a build on a Linux system, so I think changes to CVS or Subversion version control files should be omitted. After all, it's just a scratch checkout. Maybe an "export" is more appropriate.
Smaller still I quite like:
rename 'y/A-Z/a-z/' *
On case insensitive filesystems such as OS X's HFS+, you will want to add the -f flag:
rename -f 'y/A-Z/a-z/' *
A concise version using the "rename" command:
find my_root_dir -depth -exec rename 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;
This avoids problems with directories being renamed before files and trying to move files into non-existing directories (e.g. "A/A" into "a/a").
Or, a more verbose version without using "rename".
for SRC in `find my_root_dir -depth`
do
DST=`dirname "${SRC}"`/`basename "${SRC}" | tr '[A-Z]' '[a-z]'`
if [ "${SRC}" != "${DST}" ]
then
[ ! -e "${DST}" ] && mv -T "${SRC}" "${DST}" || echo "${SRC} was not renamed"
fi
done
P.S.
The latter allows more flexibility with the move command (for example, "svn mv").
for f in `find`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done
Just simply try the following if you don't need to care about efficiency.
zip -r foo.zip foo/*
unzip -LL foo.zip
One can simply use the following which is less complicated:
rename 'y/A-Z/a-z/' *
This works on CentOS/Red Hat Linux or other distributions without the rename Perl script:
for i in $( ls | grep [A-Z] ); do mv -i "$i" "`echo $i | tr 'A-Z' 'a-z'`"; done
Source: Rename all file names from uppercase to lowercase characters
(In some distributions the default rename command comes from util-linux, and that is a different, incompatible tool.)
This works if you already have or set up the rename command (e.g. through brew install in Mac):
rename --lower-case --force somedir/*
The simplest approach I found on Mac OS X was to use the rename package from http://plasmasturm.org/code/rename/:
brew install rename
rename --force --lower-case --nows *
--force Rename even when a file with the destination name already exists.
--lower-case Convert file names to all lower case.
--nows Replace all sequences of whitespace in the filename with single underscore characters.
Most of the answers above are dangerous, because they do not deal with names containing odd characters. Your safest bet for this kind of thing is to use find's -print0 option, which will terminate filenames with ASCII NUL instead of \n.
Here is a script, which only alter files and not directory names so as not to confuse find:
find . -type f -print0 | xargs -0n 1 bash -c \
's=$(dirname "$0")/$(basename "$0");
d=$(dirname "$0")/$(basename "$0"|tr "[A-Z]" "[a-z]"); mv -f "$s" "$d"'
I tested it, and it works with filenames containing spaces, all kinds of quotes, etc. This is important because if you run, as root, one of those other scripts on a tree that includes the file created by
touch \;\ echo\ hacker::0:0:hacker:\$\'\057\'root:\$\'\057\'bin\$\'\057\'bash
... well guess what ...
Here's my suboptimal solution, using a Bash shell script:
#!/bin/bash
# First, rename all folders
for f in `find . -depth ! -name CVS -type d`; do
g=`dirname "$f"`/`basename "$f" | tr '[A-Z]' '[a-z]'`
if [ "xxx$f" != "xxx$g" ]; then
echo "Renaming folder $f"
mv -f "$f" "$g"
fi
done
# Now, rename all files
for f in `find . ! -type d`; do
g=`dirname "$f"`/`basename "$f" | tr '[A-Z]' '[a-z]'`
if [ "xxx$f" != "xxx$g" ]; then
echo "Renaming file $f"
mv -f "$f" "$g"
fi
done
Folders are all renamed correctly, and mv isn't asking questions when permissions don't match, and CVS folders are not renamed (CVS control files inside that folder are still renamed, unfortunately).
Since "find -depth" and "find | sort -r" both return the folder list in a usable order for renaming, I preferred using "-depth" for searching folders.
One-liner:
for F in K*; do NEWNAME=$(echo "$F" | tr '[:upper:]' '[:lower:]'); mv "$F" "$NEWNAME"; done
Or even:
for F in K*; do mv "$F" "${F,,}"; done
Note that this will convert only files/directories starting with letter K, so adjust accordingly.
The original question asked for ignoring SVN and CVS directories, which can be done by adding -prune to the find command. E.g to ignore CVS:
find . -name CVS -prune -o -exec mv '{}' `echo {} | tr '[A-Z]' '[a-z]'` \; -print
[edit] I tried this out, and embedding the lower-case translation inside the find didn't work for reasons I don't actually understand. So, amend this to:
$> cat > tolower
#!/bin/bash
mv $1 `echo $1 | tr '[:upper:]' '[:lower:]'`
^D
$> chmod u+x tolower
$> find . -name CVS -prune -o -exec tolower '{}' \;
Ian
Not portable, Zsh only, but pretty concise.
First, make sure zmv is loaded.
autoload -U zmv
Also, make sure extendedglob is on:
setopt extendedglob
Then use:
zmv '(**/)(*)~CVS~**/CVS' '${1}${(L)2}'
To recursively lowercase files and directories where the name is not CVS.
Using Larry Wall's filename fixer:
$op = shift or die $help;
chomp(#ARGV = <STDIN>) unless #ARGV;
for (#ARGV) {
$was = $_;
eval $op;
die $# if $#;
rename($was,$_) unless $was eq $_;
}
It's as simple as
find | fix 'tr/A-Z/a-z/'
(where fix is of course the script above)
for f in `find -depth`; do mv ${f} ${f,,} ; done
find -depth prints each file and directory, with a directory's contents printed before the directory itself. ${f,,} lowercases the file name.
This works nicely on macOS too:
ruby -e "Dir['*'].each { |p| File.rename(p, p.downcase) }"
This is a small shell script that does what you requested:
root_directory="${1?-please specify parent directory}"
do_it () {
awk '{ lc= tolower($0); if (lc != $0) print "mv \"" $0 "\" \"" lc "\"" }' | sh
}
# first the folders
find "$root_directory" -depth -type d | do_it
find "$root_directory" ! -type d | do_it
Note the -depth action in the first find.
Use typeset:
typeset -l new # Always lowercase
find $topPoint | # Not using xargs to make this more readable
while read old
do new="$old" # $new is a lowercase version of $old
mv "$old" "$new" # Quotes for those annoying embedded spaces
done
On Windows, emulations, like Git Bash, may fail because Windows isn't case-sensitive under the hood. For those, add a step that mv's the file to another name first, like "$old.tmp", and then to $new.
With MacOS,
Install the rename package,
brew install rename
Use,
find . -iname "*.py" -type f | xargs -I% rename -c -f "%"
This command find all the files with a *.py extension and converts the filenames to lower case.
`f` - forces a rename
For example,
$ find . -iname "*.py" -type f
./sample/Sample_File.py
./sample_file.py
$ find . -iname "*.py" -type f | xargs -I% rename -c -f "%"
$ find . -iname "*.py" -type f
./sample/sample_file.py
./sample_file.py
Lengthy But "Works With No Surprises & No Installations"
This script handles filenames with spaces, quotes, other unusual characters and Unicode, works on case insensitive filesystems and most Unix-y environments that have bash and awk installed (i.e. almost all). It also reports collisions if any (leaving the filename in uppercase) and of course renames both files & directories and works recursively. Finally it's highly adaptable: you can tweak the find command to target the files/dirs you wish and you can tweak awk to do other name manipulations. Note that by "handles Unicode" I mean that it will indeed convert their case (not ignore them like answers that use tr).
# adapt the following command _IF_ you want to deal with specific files/dirs
find . -depth -mindepth 1 -exec bash -c '
for file do
# adapt the awk command if you wish to rename to something other than lowercase
newname=$(dirname "$file")/$(basename "$file" | awk "{print tolower(\$0)}")
if [ "$file" != "$newname" ] ; then
# the extra step with the temp filename is for case-insensitive filesystems
if [ ! -e "$newname" ] && [ ! -e "$newname.lcrnm.tmp" ] ; then
mv -T "$file" "$newname.lcrnm.tmp" && mv -T "$newname.lcrnm.tmp" "$newname"
else
echo "ERROR: Name already exists: $newname"
fi
fi
done
' sh {} +
References
My script is based on these excellent answers:
https://unix.stackexchange.com/questions/9496/looping-through-files-with-spaces-in-the-names
How to convert a string to lower case in Bash?
In OS X, mv -f shows "same file" error, so I rename twice:
for i in `find . -name "*" -type f |grep -e "[A-Z]"`; do j=`echo $i | tr '[A-Z]' '[a-z]' | sed s/\-1$//`; mv $i $i-1; mv $i-1 $j; done
I needed to do this on a Cygwin setup on Windows 7 and found that I got syntax errors with the suggestions from above that I tried (though I may have missed a working option). However, this solution straight from Ubuntu forums worked out of the can :-)
ls | while read upName; do loName=`echo "${upName}" | tr '[:upper:]' '[:lower:]'`; mv "$upName" "$loName"; done
(NB: I had previously replaced whitespace with underscores using:
for f in *\ *; do mv "$f" "${f// /_}"; done
)
Slugify Rename (regex)
It is not exactly what the OP asked for, but what I was hoping to find on this page:
A "slugify" version for renaming files so they are similar to URLs (i.e. only include alphanumeric, dots, and dashes):
rename "s/[^a-zA-Z0-9\.]+/-/g" filename
I would reach for Python in this situation, to avoid optimistically assuming paths without spaces or slashes. I've also found that python2 tends to be installed in more places than rename.
#!/usr/bin/env python2
import sys, os
def rename_dir(directory):
print('DEBUG: rename('+directory+')')
# Rename current directory if needed
os.rename(directory, directory.lower())
directory = directory.lower()
# Rename children
for fn in os.listdir(directory):
path = os.path.join(directory, fn)
os.rename(path, path.lower())
path = path.lower()
# Rename children within, if this child is a directory
if os.path.isdir(path):
rename_dir(path)
# Run program, using the first argument passed to this Python script as the name of the folder
rename_dir(sys.argv[1])
If you use Arch Linux, you can install rename) package from AUR that provides the renamexm command as /usr/bin/renamexm executable and a manual page along with it.
It is a really powerful tool to quickly rename files and directories.
Convert to lowercase
rename -l Developers.mp3 # or --lowcase
Convert to UPPER case
rename -u developers.mp3 # or --upcase, long option
Other options
-R --recursive # directory and its children
-t --test # Dry run, output but don't rename
-o --owner # Change file owner as well to user specified
-v --verbose # Output what file is renamed and its new name
-s/str/str2 # Substitute string on pattern
--yes # Confirm all actions
You can fetch the sample Developers.mp3 file from here, if needed ;)
None of the solutions here worked for me because I was on a system that didn't have access to the perl rename script, plus some of the files included spaces. However, I found a variant that works:
find . -depth -exec sh -c '
t=${0%/*}/$(printf %s "${0##*/}" | tr "[:upper:]" "[:lower:]");
[ "$t" = "$0" ] || mv -i "$0" "$t"
' {} \;
Credit goes to "Gilles 'SO- stop being evil'", see this answer on the similar question "change entire directory tree to lower-case names" on the Unix & Linux StackExchange.
I believe the one-liners can be simplified:
for f in **/*; do mv "$f" "${f:l}"; done
( find YOURDIR -type d | sort -r;
find yourdir -type f ) |
grep -v /CVS | grep -v /SVN |
while read f; do mv -v $f `echo $f | tr '[A-Z]' '[a-z]'`; done
First rename the directories bottom up sort -r (where -depth is not available), then the files.
Then grep -v /CVS instead of find ...-prune because it's simpler.
For large directories, for f in ... can overflow some shell buffers.
Use find ... | while read to avoid that.
And yes, this will clobber files which differ only in case...
find . -depth -name '*[A-Z]*'|sed -n 's/\(.*\/\)\(.*\)/mv -n -v -T \1\2 \1\L\2/p'|sh
I haven't tried the more elaborate scripts mentioned here, but none of the single commandline versions worked for me on my Synology NAS. rename is not available, and many of the variations of find fail because it seems to stick to the older name of the already renamed path (eg, if it finds ./FOO followed by ./FOO/BAR, renaming ./FOO to ./foo will still continue to list ./FOO/BAR even though that path is no longer valid). Above command worked for me without any issues.
What follows is an explanation of each part of the command:
find . -depth -name '*[A-Z]*'
This will find any file from the current directory (change . to whatever directory you want to process), using a depth-first search (eg., it will list ./foo/bar before ./foo), but only for files that contain an uppercase character. The -name filter only applies to the base file name, not the full path. So this will list ./FOO/BAR but not ./FOO/bar. This is ok, as we don't want to rename ./FOO/bar. We want to rename ./FOO though, but that one is listed later on (this is why -depth is important).
This comand in itself is particularly useful to finding the files that you want to rename in the first place. Use this after the complete rename command to search for files that still haven't been replaced because of file name collisions or errors.
sed -n 's/\(.*\/\)\(.*\)/mv -n -v -T \1\2 \1\L\2/p'
This part reads the files outputted by find and formats them in a mv command using a regular expression. The -n option stops sed from printing the input, and the p command in the search-and-replace regex outputs the replaced text.
The regex itself consists of two captures: the part up until the last / (which is the directory of the file), and the filename itself. The directory is left intact, but the filename is transformed to lowercase. So, if find outputs ./FOO/BAR, it will become mv -n -v -T ./FOO/BAR ./FOO/bar. The -n option of mv makes sure existing lowercase files are not overwritten. The -v option makes mv output every change that it makes (or doesn't make - if ./FOO/bar already exists, it outputs something like ./FOO/BAR -> ./FOO/BAR, noting that no change has been made). The -T is very important here - it treats the target file as a directory. This will make sure that ./FOO/BAR isn't moved into ./FOO/bar if that directory happens to exist.
Use this together with find to generate a list of commands that will be executed (handy to verify what will be done without actually doing it)
sh
This pretty self-explanatory. It routes all the generated mv commands to the shell interpreter. You can replace it with bash or any shell of your liking.
Using bash, without rename:
find . -exec bash -c 'mv $0 ${0,,}' {} \;

Resources