Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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

share|improve this question
    
I've spent 4 hours for this :/ it might take just a minute for you –  user251046 Jul 23 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 at 16:23
    
@drs I'm trying to rename bunch of . filejpg files to file.jpg –  user251046 Jul 23 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 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 at 16:28

3 Answers 3

up vote 7 down vote accepted

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.

share|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 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 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 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 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 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

Explanation

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>
done

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}.

share|improve this answer
    
how to rename then? –  user251046 Jul 23 at 16:26
    
can you explain please how this "one-liner" works? –  user251046 Jul 23 at 16:55
    
vote up requires 15 reputation –  user251046 Jul 23 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 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 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.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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