First, I should say that the easiest way to do this is to use the prename or rename commands.

On Ubuntu, OSX (Homebrew package rename, MacPorts package p5-file-rename), or other systems with perl rename (prename):

Copyrename s/0000/000/ F0000*

or on systems with rename from util-linux-ng, such as RHEL:

Copyrename 0000 000 F0000*

That's a lot more understandable than the equivalent sed command.

But as for understanding the sed command, the sed manpage is helpful. If you run man sed and search for & (using the / command to search), you'll find it's a special character in s/foo/bar/ replacements.

Copy  s/regexp/replacement/
         Attempt  to match regexp against the pattern space.  If success‐
         ful,  replace  that  portion  matched  with  replacement.    The
         replacement may contain the special character & to refer to that
         portion of the pattern space  which  matched,  and  the  special
         escapes  \1  through  \9  to refer to the corresponding matching
         sub-expressions in the regexp.

Therefore, \(.\) matches the first character, which can be referenced by \1. Then . matches the next character, which is always 0. Then \(.*\) matches the rest of the filename, which can be referenced by \2.

The replacement string puts it all together using & (the original filename) and \1\2 which is every part of the filename except the 2nd character, which was a 0.

This is a pretty cryptic way to do this, IMHO. If for some reason the rename command was not available and you wanted to use sed to do the rename (or perhaps you were doing something too complex for rename?), being more explicit in your regex would make it much more readable. Perhaps something like:

Copyls F00001-0708-*|sed 's/F0000\(.*\)/mv & F000\1/' | sh

Being able to see what's actually changing in the s/search/replacement/ makes it much more readable. Also it won't keep sucking characters out of your filename if you accidentally run it twice or something.

Answer from Edward Anderson on Stack Overflow
Top answer
1 of 13
182

First, I should say that the easiest way to do this is to use the prename or rename commands.

On Ubuntu, OSX (Homebrew package rename, MacPorts package p5-file-rename), or other systems with perl rename (prename):

Copyrename s/0000/000/ F0000*

or on systems with rename from util-linux-ng, such as RHEL:

Copyrename 0000 000 F0000*

That's a lot more understandable than the equivalent sed command.

But as for understanding the sed command, the sed manpage is helpful. If you run man sed and search for & (using the / command to search), you'll find it's a special character in s/foo/bar/ replacements.

Copy  s/regexp/replacement/
         Attempt  to match regexp against the pattern space.  If success‐
         ful,  replace  that  portion  matched  with  replacement.    The
         replacement may contain the special character & to refer to that
         portion of the pattern space  which  matched,  and  the  special
         escapes  \1  through  \9  to refer to the corresponding matching
         sub-expressions in the regexp.

Therefore, \(.\) matches the first character, which can be referenced by \1. Then . matches the next character, which is always 0. Then \(.*\) matches the rest of the filename, which can be referenced by \2.

The replacement string puts it all together using & (the original filename) and \1\2 which is every part of the filename except the 2nd character, which was a 0.

This is a pretty cryptic way to do this, IMHO. If for some reason the rename command was not available and you wanted to use sed to do the rename (or perhaps you were doing something too complex for rename?), being more explicit in your regex would make it much more readable. Perhaps something like:

Copyls F00001-0708-*|sed 's/F0000\(.*\)/mv & F000\1/' | sh

Being able to see what's actually changing in the s/search/replacement/ makes it much more readable. Also it won't keep sucking characters out of your filename if you accidentally run it twice or something.

2 of 13
61

You've had your sed explanation. Now you can use just the shell. No need for external commands.

Copyfor file in F0000*
do
    echo mv "$file" "${file/#F0000/F000}"
    # ${file/#F0000/F000} means replace the pattern that starts at beginning of string
done

Note that this snippet runs echo as a safety measure that prints what the mv command will do without doing it. To actually perform the mv, you need to remove echo.

Discussions

How to rename multiple files with sed?
Don't need sed, use bash. for i in *-tmp ; do mv "${i}" "${i/-tmp/.tmp}" ; done More on reddit.com
🌐 r/bash
14
6
March 18, 2022
linux - Rename multiple files in directory - Using sed or rename command - Unix & Linux Stack Exchange
I have tried so many different sed, mv and rename commands - still I cannot rename these files. There are 50 plus files - I tried commands I found - Help thx Original filenames: PAGES_TEST1_SART1.... More on unix.stackexchange.com
🌐 unix.stackexchange.com
Renaming files with sed
Hi all, I created file like this AAb.lol AAc.lol AAx.lol test.sh My goal is to create a script (test.sh) which renames all the files to their original name without AA. I want to end up with this: b.lol c.lol x.lol Using sed how is it possible? i tried to write the script #!/bin/bash for i in ... More on unix.com
🌐 unix.com
0
0
November 18, 2017
sed to rename files in a folder - please help with script
Hello, I am new to shell scripting and stuck on renaming files in a folder. The files have the format chp01_00001.wav chp01_00002.wav .... chp02_00001.wav chp02_00002.wav .... but I want them to have the following names: chp_bloomy_00001.wav chp_bloomy_00002.wav chp_bloomy_00003.wav ... More on unix.com
🌐 unix.com
8
0
October 19, 2017
🌐
Super User
superuser.com › questions › 1467293 › rename-files-with-sed-rearanging-filenames
bash - rename files with sed (rearanging filenames) - Super User
only constant pattern in names in Lecture X of Y where X and Y are numbers and may be different in different names · any idea for oneliner with sed or other regex tool? ... With just a single example, it's impossible to know what sort of pattern you are looking for. Is there always a dash? Is the Lecture X of Y phrase fixed except for different X and Y values? ... Using the rename (Debian/ubuntu) or prename (RedHat/CentOS)(aka "Larry Wall's Perl rename") command from your usual repository:
Top answer
1 of 2
2

There are a few issues here. First of all, your files are not in /00101234/, / is the root directory, kinda like Windows's C:. Your files are in ~/Desktop/images/00101234/ which means /home/yourUserName/Desktop/images/ (where yourUserName is your user name). The easiest way to deal with this, therefore, is to use relative paths. For example, consider this file:

/dir1/dir2/file

That's the absolute path to file. But if you are inside the dir1 directory, you can use a path that's relative to your current location: dir2/file.

With this in mind, let's have another look at your csv file:

/00101234/1101.jpg,/Jewellery/ALittleThankYouTeacher1101.jpg

These are relative paths. You can deal with this in two ways:

  1. Move into the ~/Desktop/images directory and use the paths as they are.
  2. Convert them to absolute paths.

I will focus on 2 since it is less likely to break. This command will not actually do anything, but it will print out the list of actions to be performed (run this from the directory containing your csv file and change yourCsv.csv to the actual name of your file):

while IFS=, read -r old new; do
    echo mv "~/Desktop/images${old}" "~/Desktop/images/Jewlery${new}"
done < yourCsv.csv

On my system, using the file you provided, this prints:

mv ~/Desktop/images/00101234/1101.jpg ~/Desktop/images/Jewlery/Jewellery/ALittleThankYouTeacher1101.jpg
mv ~/Desktop/images/00101234/1102.jpg ~/Desktop/images/Jewlery/Jewellery/ALittleThinkingOfYou1102.jpg
mv ~/Desktop/images/00101234/1155.jpg ~/Desktop/images/Jewlery/Jewellery/ALittleDreamcatcher1155.jpg
mv ~/Desktop/images/00101234/1203.jpg ~/Desktop/images/Jewlery/Jewellery/ALittleLuckyElephant1203.jpg

If that prints what you want, then we're ready to go. Remove the echo (that just means "print this", so removing it will cause the loop to execute the mv command instead of just printing it).

However, and this is important, your csv is probably a bit different. I am assuming you created it in Windows, which means it will have different line endings. So, to be on the safe side, you want to run this:

tr -d '\r' < yourCsv.csv | while IFS=, read -r old new; do
    mv "~/Desktop/images${old}" "~/Desktop/images/Jewlery${new}"
done < yourCsv.csv

Of course, I strongly recommend that you first make a backup of all of these files just in case something goes wrong.

2 of 2
0

This is an XY-problem, and at the moment the solution to problem X is not clear, but the solution to problem Y is:

To prevent read interpreting backslashes, use option -r. From help read:

-r        do not allow backslashes to escape any characters

Example

Setup:

$ echo '\00101191\XYZ123.jpg,\Homeware\TravelMugXYZ123.jpg' > files.csv

Without -r:

$ while IFS=, read orig new; do echo "$orig" "$new"; done < files.csv
00101191XYZ123.jpg HomewareTravelMugXYZ123.jpg

With -r:

$ while IFS=, read -r orig new; do echo "$orig" "$new"; done < files.csv
\00101191\XYZ123.jpg \Homeware\TravelMugXYZ123.jpg
🌐
Reddit
reddit.com › r/bash › how to rename multiple files with sed?
r/bash on Reddit: How to rename multiple files with sed?
March 18, 2022 -
  1. Sample files as follow; I just want to rename -tmp to .tmp

$ ls -1 *tmp
file-3-tmp
file-4-tmp

2. 1st attempt with sed

$ for i in *tmp; do echo $i | sed 's/-t/.t/'; done
file-3.tmp
file-4.tmp

Looks promising, but this is just echo, not the actual file. Same output as step 1. So I use sed -i but still didn't work

$ for i in *tmp; do sed -i 's/-t/.t/' $i; done
$ ls -1 *tmp
file-3-tmp
file-4-tmp

Let me know the right way to do this. Thanks

🌐
Unix.com
unix.com › shell programming and scripting
Renaming files with sed - Shell Programming and Scripting - Unix Linux Community
November 18, 2017 - Hi all, I created file like this AAb.lol AAc.lol AAx.lol test.sh My goal is to create a script (test.sh) which renames all the files to their original name without AA. I want to end up with this: b.lol c.lol x.lol Using sed how is it possible? i tried to write the script #!/bin/bash for i in $( ls ); do NewName='sed' 's/AA//g' '$i' mv '$i' $NewName done The output however is a lot of times this: From that 2nd line i can tell that $NewName is just empty.
Find elsewhere
Top answer
1 of 4
8

If you happen to have a file that looks like this:

old_name1, new_name1
old_name2, new_name2
old_name3, new_name3

You can do a dirty little trick:

sed 's/^/mv -vi "/;s/, /" "/;s/$/";/' < names.csv | bash -

The sed comamnds (delimited by semicolons) does this (s is for substitute, / is used as a delimiter, any other character would do, @ is often used as well):

  • s/^/mv -vi "/ - adds mv -vi " at the beginning of each line (^ is beginning of line);
  • s/, /" "/ - replaces comma followed by a space with " ";
  • s/$/";/ - appends double quotes to all lines ($ means end of line)

and hence the output of sed will be this:

mv -vi "old_name1" "new_name1"
mv -vi "old_name2" "new_name2"
mv -vi "old_name3" "new_name3"

which will be fed to bash to execute. The quotes around filenames are not mandatory, they just guard any possible spaces in the filenames (however they will defintely not help against spaces followed by a comma, since that is used as delimiter). the -v instructs mv to display what it is doing (in case something goes wrong, you'll know what happened), -i will cause it to ask if it were to overwrite already existing file.

2 of 4
3

There are several quoting problems in your script. It will mangle file names containing special characters: whitespace, \[?*-.

**Always use double quotes around variable substitutions "$i" and command substitutions "$(sed …)". Without double quotes, the value of the variable is interpreted as a list of whitespace-separated wildcard patterns, and each pattern is replaced by the list of matching file names (if there are no matches, the pattern is left alone).

Also, if a file name begins with -, it will be interpreted as an option by mv and possibly by echo. Pass -- first to tell the command that there will be no more options.

for i in *.ai; do
  mv -- "$i" "$(printf '%s\n' "$i" | sed 's,a1,b1,')"
done

(This still assumes that your file names don't contain newlines.) (I also fixed the sed argument into what you presumably meant.)


If your list of file names is plain comma-separated with no quotes, you can parse it with a shell script. This assumes that there are no commas or newlines in any file name.

while IFS=, read -r from to junk; do
  mv -- "$from" "$to"
done <"$files_to_rename.csv"

If your input file is a CSV file with possible quoting, use a real CSV parser. Don't try to roll your own. See Is there a robust command line tool for processing csv files? For example, in Python (with the CSV on standard input):

import csv, os, sys
for line in csv.reader(sys.stdin):
    (source, dest) = line
    os.rename(source, dest)
🌐
Commandlinefu
commandlinefu.com › commands › view › 8368 › bulk-rename-files-with-sed-one-liner
bulk rename files with sed, one-liner Using ls, sed, xargs
April 30, 2011 - ls * | sed -e 'p;s/foo/bar/' | xargs -n2 mv - (bulk rename files with sed, one-liner Renames all files in a directory named foo to bar. foobar1 gets renamed to barbar1 barfoo2 gets renamed to barbar2 fooobarfoo gets renamed to barobarfoo NOTE: Will break for files with spaces AND new lines ...
🌐
GitHub
gist.github.com › harttle › ef181c81bdc5e579485614d0d82389e9
rename files with sed · GitHub
Save harttle/ef181c81bdc5e579485614d0d82389e9 to your computer and use it in GitHub Desktop. Download ZIP · rename files with sed · Raw · fsed · This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below.
🌐
Scriptthe
scriptthe.net › 2011 › 02 › 03 › using-sed-to-mass-change-folder-or-file-names
Using sed to mass change folder or file names · ./scriptthe.net
#!/bin/bash ls | grep ‘ ’ | while read file do mv “$file” echo $file | sed 's/ /./g' done #this replaces all spaces on all dir’s (or files) in the current dir with a period. Basically, this scans through every file it finds with a space and replaces it with a . Also, I had a lot of files that have a “(year)” format. I hate ( and ) because they also are a pain in the butt in linux. So… · #!/bin/bash ls | grep ‘(’ | while read file do mv “$file” echo $file | sed 's/)//g' | sed 's/(//g' done #this will remove all the (year) style names by replacing the ( and ) with nothing.
Top answer
1 of 1
1

The command sed 's/^.*NA -//' * would not rename files, it would attempt to replace the pattern in the files' contents, streaming the result to the terminal (with potentially weird results, especially if the expansion of * includes any binary files).

The command perl -pi -e 's/^.*NA -//' * would do the same, except modifying the contents in place, possibly corrupting your files.

To use sed (or perl itself) for file renaming, you'd need to do something like

for f in *; do mv -n -- "$f" "$(sed 's/pattern/replacement' <<<"$f")"

or

for f in *; do mv -n -- "$f" "$(printf '%s\n' "$f" | sed 's/pattern/replacement')"

i.e. passing the name of each file to sed via standard input. However your pattern could just as easily be written as a shell substitution, eliminating the need for sed altogether:

for f in *; do mv -n -- "$f" "${f##* - NA - }"; done

(I'm assuming you want to remove the leading space as well). The ##* matches the longest leading character sequence, equivalent to basic regular expression ^.*

To use perl under the hood, but modifying file names rather than contents, you'd want the rename command, from package rename. (There are other rename commands, such as the one from package util-linux that work differently.) For example:

rename -n 's/^.*? - NA - //' *.aiff

(I limited the scope to .aiff files, and made the .* match non-greedy - a feature that sed does not provide - so that it only matches up to the first - NA - in case there are multiple instances.) Remove the -n once you are satisfied with the proposed re-namings.

🌐
Collecting Wisdom
collectingwisdom.com › home › how to use sed to rename multiple files
How to Use sed to Rename Multiple Files - Collecting Wisdom
May 21, 2024 - Note that we use the sed s command to substitute one pattern with another specific pattern. The following example shows how to use this syntax in practice. First, we can use the ls command to list all folders ...
🌐
Commandlinefu
commandlinefu.com › commands › view › 8374 › rename-all-files-which-contain-the-sub-string-foo-replacing-it-with-bar
Rename all files which contain the sub-string 'foo', replacing it with 'bar' Using mv
April 30, 2011 - for i in ./*foo*;do mv -- \"$i\" \"${i//foo/bar}\";done - (Rename all files which contain the sub-string 'foo', replacing it with 'bar' That is an alternative to command 8368. Command 8368 is EXTREMELY NOT clever. 1) Will break also for files with spaces AND new lines in them AND for an empty ...