What is the best way, using Bash, to rename files in the form:
(foo1, foo2, ..., foo1300, ..., fooN)
With zero-padded file names:
(foo00001, foo00002, ..., foo01300, ..., fooN)
What is the best way, using Bash, to rename files in the form:
(foo1, foo2, ..., foo1300, ..., fooN)
With zero-padded file names:
(foo00001, foo00002, ..., foo01300, ..., fooN)
In case N
is not a priori fixed:
for f in foo[0-9]*; do mv $f `printf foo%05d ${f#foo}`; done
It's not pure bash, but much easier with the rename
command:
rename 's/\d+/sprintf("%05d",$&)/e' foo*
I had a more complex case where the file names had a postfix as well as a prefix. I also needed to perform a subtraction on the number from the filename.
For example, I wanted foo56.png
to become foo00000055.png
.
I hope this helps if you're doing something more complex.
#!/bin/bash
prefix="foo"
postfix=".png"
targetDir="../newframes"
paddingLength=8
for file in ${prefix}[0-9]*${postfix}; do
# strip the prefix off the file name
postfile=${file#$prefix}
# strip the postfix off the file name
number=${postfile%$postfix}
# subtract 1 from the resulting number
i=$((number-1))
# copy to a new name with padded zeros in a new folder
cp ${file} "$targetDir"/$(printf $prefix%0${paddingLength}d$postfix $i)
done
The oneline command that I use is this:
ls * | cat -n | while read i f; do mv "$f" `printf "PATTERN" "$i"`; done
PATTERN can be for example:
%04d.${f#*.}
(keep original file extension)photo_%04d.${f#*.}
(keep original extension)%04d.jpg
photo_$(basename $f .${f#*.})_%04d.${f#*.}
You can filter the file to rename with for example ls *.jpg | ...
You have available the variable f
that is the file name and i
that is the counter.
For your question the right command is:
ls * | cat -n | while read i f; do mv "$f" `printf "foo%d05" "$i"`; done
Pure Bash, no external processes other than 'mv':
for file in foo*; do
newnumber='00000'${file#foo} # get number, pack with zeros
newnumber=${newnumber:(-5)} # the last five characters
mv $file foo$newnumber # rename
done
The following will do it:
for ((i=1; i<=N; i++)) ; do mv foo$i `printf foo%05d $i` ; done
EDIT: changed to use ((i=1,...)), thanks mweerden!
To left-pad numbers in filenames:
$ ls -l
total 0
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:24 010
-rw-r--r-- 1 victoria victoria 0 Mar 28 18:09 050
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:23 050.zzz
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:24 10
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:23 1.zzz
$ for f in [0-9]*.[a-z]*; do tmp=`echo $f | awk -F. '{printf "%04d.%s\n", $1, $2}'`; mv "$f" "$tmp"; done;
$ ls -l
total 0
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:23 0001.zzz
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:23 0050.zzz
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:24 010
-rw-r--r-- 1 victoria victoria 0 Mar 28 18:09 050
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:24 10
Explanation
for f in [0-9]*.[a-z]*; do tmp=`echo $f | \
awk -F. '{printf "%04d.%s\n", $1, $2}'`; mv "$f" "$tmp"; done;
`echo ... $2}\`
(The backslash, \, immediately above just splits that one-liner over two lines for readability)[0-9]*.[a-z]*
$f
) to pass it to awk
-F.
: awk
field separator, a period (.
): if matched, separates the file names as two fields ($1
= number; $2
= extension)printf
: print first field ($1
, the number part) as 4 digits (%04d
), then print the period, then print the second field ($2
: the extension) as a string (%s
). All of that is assigned to the $tmp
variable$f
) to the new filename ($tmp
)Here's a quick solution that assumes a fixed length prefix (your "foo") and fixed length padding. If you need more flexibility, maybe this will at least be a helpful starting point.
#!/bin/bash
# some test data
files="foo1
foo2
foo100
foo200
foo9999"
for f in $files; do
prefix=`echo "$f" | cut -c 1-3` # chars 1-3 = "foo"
number=`echo "$f" | cut -c 4-` # chars 4-end = the number
printf "%s%04d\n" "$prefix" "$number"
done
My solution replaces numbers, everywhere in a string
for f in * ; do
number=`echo $f | sed 's/[^0-9]*//g'`
padded=`printf "%04d" $number`
echo $f | sed "s/${number}/${padded}/";
done
You can easily try it, since it just prints transformed file names (no filesystem operations are performed).
A loop: for f in * ; do ;done
, lists all files and passes each filename as $f
variable to loop body.
With echo $f | sed
we pipe variable $f
to sed
program.
In command sed 's/[^0-9]*//g'
, part [^0-9]*
with modifier ^
tells to match opposite from digit 0-9 (not a number) and then remove it it with empty replacement //
. Why not just remove [a-z]
? Because filename can contain dots, dashes etc. So, we strip everything, that is not a number and get a number.
Next, we assign the result to number
variable. Remember to not put spaces in assignment, like number = …
, because you get different behavior.
We assign execution result of a command to variable, wrapping the command with backtick symbols `.
Command printf "%04d" $number
changes format of a number to 4 digits and adds zeros if our number contains less than 4 digits.
We use sed
again with replacement command like s/substring/replacement/
. To interpret our variables, we use double quotes and substitute our variables in this way ${number}
.
The script above just prints transformed names, so, let's do actual renaming job:
for f in *.js ; do
number=`echo $f | sed 's/[^0-9]*//g'`
padded=`printf "%04d" $number`
new_name=`echo $f | sed "s/${number}/${padded}/"`
mv $f $new_name;
done
Hope this helps someone.
I spent several hours to figure this out.