Batch renaming files in command line and Xargs

40,803

Solution 1

how do i rename the file so that I can just have one extension.

cd dir/with/messedup/files

for file in *.png.jpg; do
  mv "$file" "${file%.png.jpg}.jpg"
done

in the future, using xargs, how do I change the extension of the file simular to second command?

To my knowledge, that can't be done. The best way to do it would be to use a for-loop with parameter substitution much like the one above:

for file in *.png; do
  convert "$file" -resize "${file%.png}.jpg"
done

If you have files in subdirectories that you want converted, then you can pipe find to a while read loop:

find . -type f -name '*.png' |
while read file; do
  convert "$file" -resize "${file%.png}.jpg"
done

NOTE: It's generally considered a bad idea to use the output of ls in a shell script. While your example might have worked fine, there are lot's of examples where it doesn't. For instance, if your filenames happened to have newlines in them (which unix allows), ls probably won't escape those for you. (That actually depends on your implementation, which is another reason not to use ls in scripts; it's behavior varies greatly from one box to the next.) You'll get more consistent results if you either use find in a while-read loop or file globbing (e.g. *.png) in a for loop.

Solution 2

This can be also be done with xargs and sed to change the file extension.

ls | grep \.png$ | sed 'p;s/\.png/\.jpg/' | xargs -n2 mv

You can print the original filename along with what you want the filename to be. Then have xargs use those two arguments in the move command. For the one-liner, I also added a grep to filter out anything not a *.png file.

Solution 3

Coming late to the party, but here's how you can rename files with xargs. Say you have a bunch of files named fileN.svg.png and you want to name them fileN.png where N could be a series of integers:

ls *.svg.png | xargs basename -s .svg.png | xargs -I {} mv {}.svg.png {}.png

The first xargs uses basename to strip off both .svg and .png to get a just filenameN. The second xargs receives that bare name and uses replacement to rename the file.

Solution 4

To clean up your error, try the rename utility. Check the manpage for details.

In your case, you'd do rename '.png.jpg' '.jpg' ./* if your current directory is set appropriately.

Since you have convert available, I'm assuming you have mogrify too (imagemagick suite). Whenever I want to do this, I copy the files into a different directory and use mogrify instead. I usually need this only for resizing, but if you change the image format aswell, mogrify will handle the filenames (make new files with proper filenames).

You would use it as mogrify -format jpg -resize [size] ./*.png. I'm not sure what -resize without geometry arguments is supposed to do. It isn't documented and doesn't work on my machine.

As Tim Pote reasoned, I don't think you can make xargs handle filenames and extensions separately.

Solution 5

I'm late to this party by about 3 years, I just had a similar problem which I figured out myself. I had a list of png files which I converted using inkscape, because ImageMagick's svg support is poor.

I originally converted them by doing:

find . -name "*.svg" -exec inkscape {} --export-png={}.png

Which of course led to the same issue like posted above.

file1.svg
file1.svg.png
file2.svg
file2.svg.png
file3.svg
file3.svg.png
file4.svg
file4.svg.png

I wanted to rename *.svg.png to *.png, this is what I wound up with...

find . -name "*.svg.png" -print0 | sed 's/.svg.png//g' | xargs -0 -I namePrefix mv namePrefix.svg.png namePrefix.png

This does three things:

  1. find in this directory files named *.svg.png, the -print0 prints to standard output
  2. sed modifies the standard output, basically swap .svg.png with nothing, so I'd get: file1/file2/file3/file4
  3. xargs -0 to get the data from sed, -I references the filename w/o the extension, then mv original filename to new filename. The word namePrefix isn't anything special, just wanted to make it clear.

EDIT

I realize now this is the most convoluted way to do this. One can simply use the rename command.

rename 's/svg\.png/.png/' *
Share:
40,803
Cripto
Author by

Cripto

Updated on July 10, 2022

Comments

  • Cripto
    Cripto almost 2 years

    So, I have the following structure:

    .
    ..
    a.png
    b.png 
    c.png
    

    I ran a command to resize them

    ls | xargs -I xx convert xx -resize xx.jpg
    

    Now my dir looks like this

    .
    ..
    a.png.jpg
    a.png
    b.png.jpg
    b.png
    c.png.jpg
    c.png
    

    The firs question is, how do i rename the file so that I can just have one extension. Not two. (basically, how do I clean up my original mistake)?

    The second question is, in the future, using xargs, how do I change the extension of the file simular to second command?

  • Matej
    Matej almost 12 years
    I think piping find into a while loop is genius, I've never seen it before.
  • rturrado
    rturrado over 8 years
    Beautiful! Didn't know about sed 'p' nor about xargs -n2.
  • adrianN
    adrianN almost 8 years
    Does this work if the paths contain spaces? I don't think so. find . -name "*.png" -print0 | sed 'p;s/\.png/\.jpg/' | xargs -0 -n2 mv is better I think.
  • Alan Zeino
    Alan Zeino over 5 years
    rename definitely was the simplest way for me: $ find . -name *[email protected] | rename 's/_12_0\@2x.png/_12_0\@3x.png/' --stdin (in this case, I wanted to change the 2x to `3x in the file name)
  • Phil
    Phil over 5 years
    I recommend using "sed -z", if you use null-byte actions. Otherwise sed will interprete the stream as binary. Less performant and more error prone.
  • meh
    meh over 5 years
    By far the best answer here.
  • james-see
    james-see over 4 years
    You are never late on the internet. This was super helpful, I used a modified version of your one-liner. Thanks
  • soMuchToLearnAndShare
    soMuchToLearnAndShare almost 4 years
    this only works if the command is run under the folder which these files are located... .my attempt to use this, the mv command complains as it will not be able to 'stat' the file,
  • Alexander Vasiljev
    Alexander Vasiljev over 3 years
    I'd start with ... | xargs -0 -n2 echo mv first, and proceed with ... | xargs -0 -n2 mv if result is correct.
  • CervEd
    CervEd about 3 years
    any reason to use mv instead of rename?
  • Ferd
    Ferd about 2 years
    @adrianN Does find ... -print0 work with GNU sed? Even though this question is tagged #linux, this version works for me with BSD sed on macOS: find . -name "*.png" | sed -e 'p;s/\.png/\.jpg/' | tr '\n' '\0' | xargs -0 -n2 mv
  • damd
    damd almost 2 years
    @CervEd Not all systems have rename, and not all systems have the same rename