I'm trying to change jpg files named with a dot and a space at the beginning and with a missing dot before the end (like this . Startjpg to Start.jpg

The dot makes the files hidden from sed and ls -al lists those files so I'm piping ls -al to sed. I've read through many manuals online and I always get this error

sed: -e expression #1, char 6: unknown command: `/'

I've tried

ls -al | sed -r '/^\./*.*/g'
ls -al | sed -r '/^\.//g'
ls -al | sed -r '/.*/[\.]g'
ls -al | sed 's/^\./\\1*./g'

and many more and even many more many more.

also I need to change filejpg to file.jpg

  • I've spent 4 hours for this :/ it might take just a minute for you – user251046 Jul 23 '14 at 16:20
  • You say you are also trying to change filejpg to file.jpg. What are you trying to do in the first place? – drs Jul 23 '14 at 16:23
  • @drs I'm trying to rename bunch of . filejpg files to file.jpg – user251046 Jul 23 '14 at 16:24
  • There is a rename or prename command (part of Perl) which is a much better tool for this job. Note that there is a non-perl rename tool also sometimes called rename, it's much less powerful. – derobert Jul 23 '14 at 16:28
  • @user251046, give us the exact input and the exact output. Also let us know what you are trying to do by editing your question. – Ramesh Jul 23 '14 at 16:28

With the Perl rename tool (which is called rename on Debian and friends including Ubuntu, it may be prename elsewhere):

rename -n 's/(?<!\.)jpg$/.jpg/' *  # -n makes it show you what it'll do,
                                   # but not actually do it. Remove the -n to
                                   # actually rename

To break down that patter: the jpg$ means "ends with 'jpg'". The (?<!\.) means 'there isn't a dot before that 'jpg'". That prevents you from changing foo.jpg into foo..jpg, which would be silly.

The * is the normal shell wildcard; rename takes a list of files to consider renaming. You can of course do /path/to/dir/*, pass a list of file names, use in conjunction with find, etc.

Removing dots and spaces from the beginning is fairly easy too:

rename -n 's/^[. ]+//' *          # trying -n first is good practice

That will remove all dots and spaces at the beginning. It'll turn . . . foo into foo.

Normally, * shell expansion won't yield files that have a name starting with a dot (hidden files). One option is to use .*; that'll also yield the two special entries . (current directory) and .. (parent directory). That should be harmless in this case; the first command will ignore them (they don't end in jpg); the second command will try to rename them, but that should just produce an error. An alternative is find:

find -type f -exec rename -n 's/^[. ]+//' '{}' +

-type f will limit to only files. You can of course also use any of find's other options as well.

| improve this answer | |
  • The -n option doesn't list anything and without it it unfortunately doesn't rename anything.I guess that's because the files are hidden to it. – user251046 Jul 23 '14 at 16:37
  • @user251046 try it with .* instead of *. That'll wind up passing it the special entries . and .. as well, but it should ignore those. – derobert Jul 23 '14 at 16:39
  • How did you learn all this? You're amazing! Maybe you can show me where you found this out? Also I have now one file named IMG_32347 how to add .jpg to it? – user251046 Jul 23 '14 at 16:48
  • @user251046 if its one file, by far the easiest way is mv IMG_32347 IMG_32347.jpg. I learned all of this from using Linux for over 15 years now, and Perl for even longer. – derobert Jul 23 '14 at 16:53
  • Thank You for your reply, I know I can just do mv but I'm asking because this is useful to know. This is what I've just tried (and failed) rename -n 's/(?<!\.flac)/.flac\$/' * – user251046 Jul 23 '14 at 16:59

While sed is a very useful and versatile tool, you're not using it properly. It's best used to match and substitute strings in text files; it can't directly rename files on the filesystem.

This task is better suited for a bash one-liner (assuming that's your shell). To rename something like . filejpg to file.jpg, use this:

find . -name '. *' -print0 | while read -d $'\0' file; do short_file=${file%jpg}.jpg; mv "$file" "${short_file:4}"; done


find is a program that returns paths to files that match a certain file property, in this case the file's name. If all you wanted to know was what .jpg files are in subdirectories of your current path, you would do find . -name "*.jpg". Normally this outputs each file on a new line. The -print0 makes it separate the matches with a null character instead. This allows for proper handling of filenames with spaces when we pass the output to the next commands.

The | character is known as a pipe. It tells the shell to take the output of the command on the left and pass it as the input to the command on the right, in this case, the read command within the while loop.

The read command is used to take the output of find and assign it to a variable, file. Normally this would assign values to file each word at a time, but the -d $'\0'$ causes the assignments to be delimited by the null character (matching how we delimited the files in find by using the -print0 flag).

The while loops causes read to iteratively assign values to "file" for each matching filename. The do and done are part of the standard bash syntax for a while loop:

while <something is true>; do
    <run some commands>

In this case, our "run some command" first fixes the extension of the file name and assigns it to a new variable: short_file=${file%jpg}.jpg changes . filejpg to . file.jpg. Then it runs mv to rename the file, removing the . at the beginning of the extension-corrected filename: mv "$file" ${short_file:2}.

| improve this answer | |
  • how to rename then? – user251046 Jul 23 '14 at 16:26
  • can you explain please how this "one-liner" works? – user251046 Jul 23 '14 at 16:55
  • vote up requires 15 reputation – user251046 Jul 23 '14 at 17:09
  • Thanks for your thorough explanation.I don't understand this part though *Normally this would assign values to file each word at a time, but the -d $'\0'$ causes the assignments to be delimited by the null character (matching how we delimited the files in find by using the -print0 flag).*It works, how to select this as my accepted answer too? – user251046 Jul 23 '14 at 17:46
  • you can only select one answer to each question. derobert's answer is a little cleaner so it's probably better to leave it as the selected one. – drs Jul 23 '14 at 18:19

As an alternative to the other answers, you can use the graphical tool GPRename. It can replace characters, truncate filenames, etc. The advantage is that there's a built-in preview function to check the new filenames before renaming them. But since it works on one directory at a time, it will be inconvenient to use it with numerous folders in sub-folders. But for less than 10 folders it's not a problem.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.