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?
The xargs command is used in a UNIX shell to convert input from standard input into arguments to a command. In other words, through the use of xargs the output of a command is used as the input of another command.
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.
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.
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.
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.
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:
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/' *
After some investigation on similar task here is my code:
find . -maxdepth 1 -name '*.png' -print0 | sed 's/.png//g' | xargs -0 -I% -n 1 -P 8 convert -quality 100 %.png %.jpg
Reasoning:
convert
instead of mv
) ls \.png$ | xargs
will not deal with spaces in the path/filename find .
will search in sub-folders, so use -maxdepth 1
convert
doesn't use available CPUs so -P8
(or -P other
) sed
without 'g'
at the end will not substitute all files (only one) sed 's/.png//g'
will leave no extension (basename could also work but didn't after -print0
)parallel
- potentially better solution but didn't work on my Ubuntu 18.04 bash 4.4%
is the smallest common symbol for substitution (compare to {} xx namePrefix
) -n2
parameter is good for xargs
but didn't work with -print0
properly (n
number of entries to take and pass after xargs
)-quality 100
default magic quality is 92 (which is fine), here 100% to avoid loosing anythingMy attempt from: https://www.tecmint.com/linux-image-conversion-tools/
ls -1 *.png | xargs -n 1 bash -c 'convert "$0" "${0%.png}.jpg"'
Using parallel
parallel convert '{}' '{.}.jpg' ::: *.png
My solution is similar to many of the xarg solutions, and particularly similar to Schleis'.
The difference here is a full regex manipulation with match references, and sed commands that properly ignore files that don't match so you don't need to prefilter your listing.
This is also safe for files with spaces and shell meta.
Change \2
in the replacement to any desired extension.
ls |
sed -nE 's/Rick\.and\.Morty\.(S03E[0-9]{2})\..*(\.[a-z0-9]{3})/"&" "Rick and Morty \1\2"/;T;p' |
xargs -n 2 mv
The -n
arg tell's sed not to print anything by default, the T
command says skip to the end of the script if the previous s
command didn't do a replacement, the p
command prints the pattern space (only hit if the s
command matches).
The &
in the replacement is a reference to the contents of the original filename match.
If we replace mv
in the command with bash -c 'echo "run($#) $@"' bash
then we can see the number of times mv
would be called, and with parameter count and value:
$ ls |
sed -nE 's/Rick\.and\.Morty\.(S03E[0-9]{2})\..*(\.[a-z0-9]{3})/"&" "Rick and Morty \1\2"/;T;p' |
xargs -n 2 bash -c 'echo "run($#) $@"' bash
run(2) Rick.and.Morty.S03E02.720p.HDTV.x264-BATV.mkv Rick and Morty S03E02.mkv
run(2) Rick.and.Morty.S03E03.720p.HDTV.x264-BATV.mkv Rick and Morty S03E03.mkv
run(2) Rick.and.Morty.S03E04.720p.HDTV.x264-BATV.mkv Rick and Morty S03E04.mkv
run(2) Rick.and.Morty.S03E05.HDTV.x264-BATV[ettv].mkv Rick and Morty S03E05.mkv
run(2) Rick.and.Morty.S03E06.720p.HDTV.x264-BATV.mkv Rick and Morty S03E06.mkv
run(2) Rick.and.Morty.S03E06.HDTV.x264-BATV.mkv Rick and Morty S03E06.mkv
run(2) Rick.and.Morty.S03E07.720p.HDTV.x264-BATV[ettv].mkv Rick and Morty S03E07.mkv
run(2) Rick.and.Morty.S03E08.720p.HDTV.x264-BATV.mkv Rick and Morty S03E08.mkv
run(2) Rick.and.Morty.S03E09.720p.HDTV.x264-BATV.mkv Rick and Morty S03E09.mkv
run(2) Rick.and.Morty.S03E10.720p.HDTV.x264-BATV.mkv Rick and Morty S03E10.mkv
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With