To replace # by somethingelse for filenames in the current directory (not recursive) you can use the (Perl-)rename utility:
rename 's/#/somethingelse/' *
Characters like - must be escaped with a \.
For your case, you would want to use
rename 's/#U00a9/safe/g' *
Note that if you only want to operate on a certain selection of files, e.g., only *.jpg, adjust the final input to match that selection:
rename 's/#U00a9/safe/g' *.jpg
To perform a test before actually changing filenames, use the -n flag:
demo/> ls
Lucky-#U00a9NBC-125x125.jpg
Lucky-#U00a9NBC-150x150.jpg
demo/> rename -n 's/#U00a9/safe/g' *.jpg
rename(Lucky-#U00a9NBC-125x125.jpg, Lucky-safeNBC-125x125.jpg)
rename(Lucky-#U00a9NBC-150x150.jpg, Lucky-safeNBC-150x150.jpg)
For OS X, rename can be installed using homebrew: brew install rename.
To replace # by somethingelse for filenames in the current directory (not recursive) you can use the (Perl-)rename utility:
rename 's/#/somethingelse/' *
Characters like - must be escaped with a \.
For your case, you would want to use
rename 's/#U00a9/safe/g' *
Note that if you only want to operate on a certain selection of files, e.g., only *.jpg, adjust the final input to match that selection:
rename 's/#U00a9/safe/g' *.jpg
To perform a test before actually changing filenames, use the -n flag:
demo/> ls
Lucky-#U00a9NBC-125x125.jpg
Lucky-#U00a9NBC-150x150.jpg
demo/> rename -n 's/#U00a9/safe/g' *.jpg
rename(Lucky-#U00a9NBC-125x125.jpg, Lucky-safeNBC-125x125.jpg)
rename(Lucky-#U00a9NBC-150x150.jpg, Lucky-safeNBC-150x150.jpg)
For OS X, rename can be installed using homebrew: brew install rename.
This is not hard, simply make sure to escape the octothorpe (#) in the name by prepending a reverse-slash (\).
find . -type f -name 'Lucky-*' | while read FILE ; do
newfile="$(echo ${FILE} |sed -e 's/\\#U00a9/safe/')" ;
mv "${FILE}" "${newfile}" ;
done
An example to help you get off the ground.
for f in *.jpg; do mv "$f" "$(echo "$f" | sed s/IMG/VACATION/)"; done
In this example, I am assuming that all your image files contain the string IMG and you want to replace IMG with VACATION.
The shell automatically evaluates *.jpg to all the matching files.
The second argument of mv (the new name of the file) is the output of the sed command that replaces IMG with VACATION.
If your filenames include whitespace pay careful attention to the "$f" notation. You need the double-quotes to preserve the whitespace.
You can use rename utility to rename multiple files by a pattern. For example following command will prepend string MyVacation2011_ to all the files with jpg extension.
rename 's/^/MyVacation2011_/g' *.jpg
or
rename <pattern> <replacement> <file-list>
Videos
A simple 1-liner should do (assumes Posix sh-compatible shell):
for f in *:*; do mv -v -- "$f" "$(echo "$f" | tr ':' '-')"; done
Explanation:
for ... in ...; do ...; doneis a loop*:*matches all files and directories in the the current directory which have:in their namefis assigned in turn to each such file name in the loopmvrenames its first argument to the second one;-v(verbose) asks it to print what it does; this option is GNU-utils specific, so it is available on Linux but not Solaris$(...)executes the code in a sub-shell and substitutes the outputechoprints its argument to the standard outputtrreads standard output and translates the characters according to the supplied map
If you are using bash,
you can avoid spawning an extra shell ($()) with sub-processes (tr)
by replacing $(...) with ${f//:/-}.
As stated in another post by me the Perl-based rename tool (sometimes called prename, not to be confused with the Linux native rename tool) could do the trick for you. You just need to type
rename s/:/-/g <files to rename>
This replaces every colon with a dash in all files you name at the end, i. e. 2013-10-*. Remove the g to only replace the first colon.
Here's the link to my other Post
How about this:
find . -name "*HEX20*" -exec rename HEX20 HEX8 '{}' +
This will search recursively through the current directory and any subdirectories to match HEX20. (The flag -type f is omitted because the asker wants to change the names of directories in addition to files.) It will then build a long rename command and ultimately call it. This type of construction may be simpler than building a series of commands with sed and then executing them one-by-one.
Try this:
find . -type f -name "*HEX20*" | sed 's/\(.*\)HEX20\(.*\)/mv \0 \1HEX8\2/' | sh
This way you find for regular files having HEX20 in their names:
find . -type f -name "*HEX20*"
then change the last occurrence of HEX20 whith HEX8 and compile the mv command:
find . -type f -name "*HEX20*" | sed 's/\(.*\)HEX20\(.*\)/mv \0 \1HEX8\2/'
finally you execute the compiled commands with sh:
find . -type f -name "*HEX20*" | sed 's/\(.*\)HEX20\(.*\)/mv \0 \1HEX8\2/' | sh
This will do the job:
Firstly: Install rename by running the following command in the terminal:
sudo apt install rename
Secondly: In the terminal,cd to the directory containing your files.
Finally: Rename the files to your desired format by running the following command in the terminal:
rename 's/^(.+)\.(.+)\.(.+)\.(.+)\.(.+)\.mp4
1$2.mp4/' *
Done
Notice:
To see how the rename command will operate on your files but without really renaming them ( just print the output to the terminal ), you can add the option -n after it. Like so:
rename -n 's/^(.+)\.(.+)\.(.+)\.(.+)\.(.+)\.mp4
1$2.mp4/' *
Explanation - as requested by Hamza:
Part # 1:
's/ORIGINAL/NEW/'
Substitutes the ORIGINAL string with the NEW string.
To see how it works as simple as it gets:
- Please run in the terminal
rename -n 's/file.number1.2010.720p.otherinfo.mp4/NEW.mp4/' * - Assuming you have a file named
file.number1.2010.720p.otherinfo.mp4in the current directory. - The output would be
rename(file.number1.2010.720p.otherinfo.mp4, NEW.mp4)
Part # 2:
^(.+)\.(.+)\.(.+)\.(.+)\.(.+)\.mp4$
Starts at the beginning of the string ^ and then matches one or more character/s ( any character ) (.+) before the dot \.
This group is put into a variable $1 and repeated four more times ( five in total ) each group is put into a variable ( $2, $3, $4, $5 ) until .mp4 represented as \.mp4 is reached.
Makes sure the string ends with .mp4 by using the $ symbol which matches the end of the string.
This part is, however, a bit flexible and will give you undesired results if the file naming is inconsistent and you have files with more than five parts separated by dots in their names like file.number1.2010.720p.otherinfo.extrainfo.mp4
If this is the case, please substitute this part with the more strict regular expression below. This way it will only operate on files with five parts separated by dots in their names only:
^([^.]+)\.([^.]+)\.([^.]+)\.([^.]+)\.([^.]+)\.mp4$
Part # 3:
2.mp4
Defines the new file name as what is under the variable for the first group $1 ( in this case file ) + what is under the variable for the second group $2 ( in this case number(x) ) + .mp4
Part # 4:
*
Operates on all the files in the current directory.
You can use cut and bash string manipulation:
First approach: only with cut:
for I in .*mp4
do
echo mv "
(echo $I | cut -d. -f 1,2,6)"
done
Explanation:
for...will select all.mp4filesecho $I |will convert theIvariable into an input for the next programcut -d. -f 1,2,6will select fields 1, 2 and 6 where fields are separated by.
But this approach can be problematic since, the 6th field may be not the extension.
So you can have a second way:
for I in .*mp4
do
echo mv "
(echo $I | cut -d. -f 1,2).mp4"
done
With that command, you force the extension to be .mp4. It will work, but what if you have not only mp4 files with the same naming pattern?
In that case, you can use extension selection:
for I in *mp4 *avi
do
echo mv "
(echo
{I##*.}"
done
${I##*.}will allow you to select what is behind the last.in variableI
Note: to prevent bad behavior, I added echo in the commands to see what they will do before running them really.
rename 's/ACDC/AC-DC/' *.xxx
from man rename
DESCRIPTION
"rename" renames the filenames supplied according to the rule specified as the
first argument. The perlexpr argument is a Perl expression which is expected to modify the
$_ string in Perl for at least some of the filenames specified. If a given filename is not
modified by the expression, it will not be renamed. If no filenames are given on
the command line, filenames will be read via standard input.
For example, to rename all files matching "*.bak" to strip the extension, you might say
rename 's/\.bak$//' *.bak
To translate uppercase names to lower, you'd use
rename 'y/A-Z/a-z/' *
This answer contains the good parts from all other answers, while leaving out such heresy as ls | while read.
Current directory:
for file in ACDC*.xxx; do
mv "$file" "${file//ACDC/AC-DC}"
done
Including subdirectories:
find . -type f -name "ACDC*" -print0 | while read -r -d '' file; do
mv "$file" "${file//ACDC/AC-DC}"
done
Newline characters are really unlikely to be in filenames, so this can be simpler while still working with names containing spaces:
find . -type f -name "ACDC*" | while read -r file; do
mv "$file" "${file//ACDC/AC-DC}"
done
If you can use Bash, then the following script should do what you want:
#!/bin/bash
(( $# != 2 )) && exit 1
for f in *; do
newf="${f//$1/$2}"
if [[ $f != $newf ]]; then
mv "
newf"
fi
done
It tries to replace filename with new string, if resulted filename doesn't match original, then it renames the file to resulted filename.
Usage, if saved as replacestr and given mode to be executed:
$ ./replacestr abc def
It will try to rename all. You can use [[ ! -f $f ]] to skip non-file. You can also wrap it in function and source it from your ~/.bashrc if you need this very often.
Here's a nice howto. There are many recipies, and even a script. I hope you'll find what you're looking for.
Actually you should study Regular expressions for better understanding of the topic.
You can do it this way:
find . -name '123_*.txt' -type f -exec sh -c '
for f; do
mv "$f" "${f%/*}/${f##*/123_}"
done' sh {} +
No pipes, no reads, no chance of breaking on malformed filenames, no non-standard tools or features.
find . -name "123*.txt" -exec rename 's/^123_//' {} ";"
will do it. No AWK, no for, no xargs needed, but rename, a very useful command from the Perl lib. It is not always included with Linux, but is easy to install from the repos.
If your structure only has two levels, you don't need to use recursion.
Don't parse filenames with sed. If you want to use regex and sed style syntax to rename files, use rename. If you're using Ubuntu 17.10, you need to install it
sudo apt install rename
Then you can use pa4080's answer.
With rename, use the -n option for testing.
rename -n 'expression' file(s)
You could also just use the mv command. As a one-line command:
for d in ./*/; do mv -v "$d" "${d/Edition/Volume}"; done; for f in ./*/*; do mv -v "$f" "${f/Edition/Volume}"; done
You can use echo for testing with mv, ie echo mv -v source dest, but it gives inaccurate results here, unless you test and then run the loops separately.
As a script:
#!/bin/bash
# rename directories first
for d in ./*/; do
mv -v "$d" "${d/Edition/Volume}"
done
# now rename files
for f in ./*/*; do
mv -v "$f" "${f/Edition/Volume}"
done
mv and rename recognise -- to indicate the end of options. Since all paths begin with ./ in this example, we do not need to use that, but if paths may begin with -, then use -- at the end of options, eg rename -n -- 's/foo/bar/' *, to prevent those paths being interpreted as options.
You could use the command rename two times to accomplish this task:
rename 's/Edition/Volume/' */ # rename the directories
rename 's/Edition/Volume/' */*.pdf # rename the PDF files inside
Here are two similar questions:
- Recursive bash script
- Explaining a shell script to recursively print full directory tree
With only shell, using parameter expansion:
for f in *456*; do echo mv -i -- "$f" "${f//456/555}"; done
here we re iterating over the files having 456 in their names, and the parameter expansion pattern ${f//456/555} will replace all 456 substrings in the filename with 555.
The above will do the dry-run by showing what mv command will be run, you can remove echo to let the action take place:
for f in *456*; do mv -i -- "$f" "${f//456/555}"; done
With rename (prename):
rename -n 's/456/555/g' *456*
this will replace all (g) 456 substring from filenames with 555 (s/456/555/).
-n will do the dry-run, if you are satisfied with the changes to be made, remove -n to let the actual renaming take place:
rename 's/456/555/g' *456*
It looks like you're forgetting to pass the files to rename.
rename 456 555 *456*
In any shell, you can loop over the files whose name contains a space. Replacing the spaces with underscores is easy in bash, ksh and zsh with the ${VARIABLE//PATTERN/REPLACEMENT} construct.
for x in *" "*; do
mv -- "$x" "${x// /_}"
done
On Debian, Ubuntu and derivatives, you can use the Perl rename (other distributions ship a different program as rename, and that program isn't helpful here).
rename 's/ /_/g' ./*
An obligatory zsh solution:
autoload zmv
zmv '(*)' '${1// /_}'
Or:
autoload zmv
zmv '*' '${f// /_}'
An obligatory POSIX solution:
for x in *" "*; do
y=$(printf %s/ "$x" | tr " " "_")
mv -- "$x" "${y%/}"
done
If you need to rename files in subdirectories as well, and your find supports the -execdir predicate, then you can do
find /search/path -depth -name '* *' \
-execdir bash -c 'mv -- "$1" "${1// /_}"' bash {} \;
Thank to @glenn jackman for suggesting -depth option for find and to make me think.
Note that on some systems (including GNU/Linux ones), find may fail to find files whose name contains spaces and also sequences of bytes that don't form valid characters (typical with media files with names with non-ASCII characters encoded in a charset different from the locale's). Setting the locale to C (as in LC_ALL=C find...) would address the problem.
Using find and rename:
find . -type f -exec rename 's/string1/string2/g' {} +
The find . -type f part of the command means to search for all files (-type f) in the current directory (.).
The
-execoption tellsfindto execute a command on each file it finds.The
renamecommand is used to rename files, and the syntax used here is for the Perl version ofrename.The
's/string1/string2/g'is a regular expression that specifies what to search for and what to replace it with. In this case,string1is the string to be replaced, andstring2is the replacement string. The/gat the end means to replace all occurrences ofstring1in the filename.The
{}symbol is a placeholder for the filename thatfindhas found.The
+at the end of the command tellsfindto pass multiple filenames at once to the rename command, which is more efficient than invoking rename for each individual file.
So, overall, this command searches for all files in the current directory and executes the rename command to replace all occurrences of string1 with string2 in the filename for each file found.
you can replace all the file names using for and mv command.
here i am replacing all the text files starting with abc names with xyz
for file in abc*.txt; do mv -v "$file" "${file/abc/xyz}"; done
the below command will replace the files recursively in all folders
for file in `find . -type f -name 'abc*.txt'`; do mv -v "$file" "${file/abc/xyz}"; done
You can use the rename command (see edit 1).
Solution 1
For a reasonable number of files/directory, by setting bash 4 option globstar (not works on recursive name, see edit 3):
shopt -s globstar
rename -n 's/etckeeper/userkeeper/g' **
Solution 2
For a big number of files/directories using rename and find in two steps to prevent failed rename on files in just renamed directories (see edit 2):
find . -type f -exec rename 's/etckeeper/userkeeper/g' {} \;
find . -type d -exec rename 's/etckeeper/userkeeper/g' {} \;
EDIT 1:
There are two different rename commands. This answer uses the Perl-based rename command, available in Debian-based distros. To have it on a not Debian based distro you can install from cpan or grab it around.
EDIT 2:
As pointed out by Jeff Schaller in the comments the -depth find option Process each directory's contents before the directory itself so only an "ordered" find by -depth option would be enough:
find . -depth -exec rename 's/etckeeper/userkeeper/g' {} \;
EDIT 3
Solution 1 doesn't work for recursive rename targets, (es. etckeeper/etckeeper/etckeeper) becasue outer levels are processed before inner levels and pointer to inner levels become useless. (After the first rename etckeeper/etckeeper/etckeeper will be named usrkeeper/etckeeper/etckeeper so the rename for etckeeper/etckeeper/ and etckeeper/etckeeper/etckeeper will fail). The same problem fixed in find by -depth options.
EDIT4
As pointed out in the comments by cas, I'd use {} + rather than {} \; - forking a perl script like rename multiple times (once per file/dir) is expensive.
Your original question is actually pretty easy to answer: xargs (at least on OS X) has an -I option, too, which does not require that the replacement string be a unique argument. So, the invocation simply becomes:
% find . -path '*etckeeper*' -print0 | xargs -0 -n 1 -I % bash -c 'echo mv % $(echo % | sed "s/etckeeper/usrkeeper/g" )'
Easy peasy. Now, let's rename in the correct order. As it turns out, find will print directories first (because it has to process them before it descends into them), so all we need to do is reverse the order of the filenames:
% find . -path '*etckeeper*' -print | tail -r | xargs -n 1 -I % bash -c 'echo mv % $(echo % | sed "s/etckeeper/usrkeeper/g" )'
Note that I've switched find -print0 | xargs -0 for find -print | xargs. Why? Because tail -r reverses based on newlines, not null characters. If you didn't switch it, tail -r would print out the same thing you piped into it! So the script is more correct now, but also it will break on filenames that contain e.g. newlines.
Note that if you don't have tail -r, you should try tac. See: How can I reverse the order of lines in a file? on Stack Overflow.
Now, the issue is that the sed command is too aggressive. For example, with a tree like:
.
โโโ etckeeper-foo
โ โโโ etckeeper-bar.md
โโโ some-other-directory
โโโ etckeeper-baz.md
You'll get mv invocations that look like:
mv ./some-other-directory/etckeeper-baz.md ./some-other-directory/usrkeeper-baz.md
mv ./etckeeper-foo/etckeeper-bar.md ./usrkeeper-foo/usrkeeper-bar.md
mv ./etckeeper-foo ./usrkeeper-foo
The first one's fine, but the second one's clearly not - since we haven't yet done mv ./etckeeper-foo ./usrkeeper-foo!
Let's only replace in the last pathname component. We can do this by using basename and dirname:
% find . -name '*etckeeper*' -print | tail -r | xargs -n 1 -I % bash -c 'echo mv % $(dirname %)/$(basename % | sed "s/etckeeper/usrkeeper/g" )'
Note that the other change I've made is using find -name, not find -path.
This produces the correct mv invocations:
mv ./some-other-directory/etckeeper-baz.md ./some-other-directory/usrkeeper-baz.md
mv ./etckeeper-foo/etckeeper-bar.md ./etckeeper-foo/usrkeeper-bar.md
mv ./etckeeper-foo ./usrkeeper-foo
Finally. Remember, once again, that this will fail on esoteric filenames. Note also that if your filenames are particularly long, xargs will not work properly because the arguments to mv become too long. You can (at least on OS X) pass -x to xargs to tell it to immediately fail if this is the case.
You'll need to do this in several steps
- Generate list of files containing the string you want to change (e.g.,
find mydir -type f -print | xargs egrep <searchstring>) - For each of those files, do the substitutions (e.g.,
| while read fn; do sed 's/searchstring/replacement/g' $fn >/tmp/foo && mv /tmp/foo $fn; done) - Now find directory names to change (e.g.,
find mydir -type d -print | egrep <searchstring>) - and change them (e.g.,
| while read olddir; do newdir=echo $olddir | sed 's/searchstring/replacement/g'; mv $olddir $newdir; done) (there should be backtics around the echo | sed newdir assignment; I'd appreciate a comment on how to include backtics in an inline code segment) - And now filenames, since the directory names won't change. Very similar to the directory change, only the initial
findshould be-type f
That should be enough to get you going.
A quick and dirty way of doing this could be:
Get a list of all files that match your pattern
# find /path -name \*pattern\* > filelistIterate through filelist with a shell script, doing whatever you want to each line:
#!/bin/sh for I in `cat filelist` do # for renaming, use mv mv $I new.file.name done
for file in *\#015
do
mv -- "$file" "${file%\#015}"
done
You may need to escape the "\"s. Try it in a tmp directory first.
If you have rename installed, this becomes a fairly simple task:
rename 's/\\#015$//' /root/path/*\\#015
You can add the -f flag to force overwriting existing files if necessary.