Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to increment a file name in the bash shell?

120 views
Skip to first unread message

Seamus Okafor

unread,
Aug 31, 2014, 2:54:19 PM8/31/14
to
I want to move a series of files to numerical names, e.g.,
mv firstfile.jpg file001.jpg
mv secondfile.jpg file002.jpg
mv thirdfile.jpg file003.jpg
etc.

I know the syntax for a for loop, e.g.,
$ for i in *.jpg do; mv $i $i_00#.jpg; done

But, how do I increment the number to be 1, and then 2,
and then 3, etc.?

Lew Pitcher

unread,
Aug 31, 2014, 3:12:07 PM8/31/14
to
On Sunday 31 August 2014 14:54, in alt.os.linux, "Seamus Okafor"
using the $(()) syntax, you can increment a variable.

15:08 $ COUNT=1
15:11 $ echo $COUNT
1

15:11 $ COUNT=$((COUNT + 1))
15:11 $ echo $COUNT
2

15:11 $ COUNT=$((COUNT + 1))
15:11 $ echo $COUNT
3

15:11 $



--
Lew Pitcher
"In Skills, We Trust"
PGP public key available upon request

Lew Pitcher

unread,
Aug 31, 2014, 3:32:30 PM8/31/14
to
On Sunday 31 August 2014 15:12, in alt.os.linux, "Lew Pitcher"
<lew.p...@digitalfreehold.ca> wrote:

> On Sunday 31 August 2014 14:54, in alt.os.linux, "Seamus Okafor"
> <Seamus...@is.invalid> wrote:
>
>> I want to move a series of files to numerical names, e.g.,
>> mv firstfile.jpg file001.jpg
>> mv secondfile.jpg file002.jpg
>> mv thirdfile.jpg file003.jpg
>> etc.
>>
>> I know the syntax for a for loop, e.g.,
>> $ for i in *.jpg do; mv $i $i_00#.jpg; done
>>
>> But, how do I increment the number to be 1, and then 2,
>> and then 3, etc.?
>
> using the $(()) syntax, you can increment a variable.
>
> 15:08 $ COUNT=1
> 15:11 $ echo $COUNT
> 1
>
> 15:11 $ COUNT=$((COUNT + 1))
> 15:11 $ echo $COUNT
> 2
>
> 15:11 $ COUNT=$((COUNT + 1))
> 15:11 $ echo $COUNT
> 3
>
> 15:11 $

So, your loop might look like
FILENO=1
for i in *.jpg ;
do
mv "$i" "file${FILENO}.jpg"
FILENO=$((FILENO + 1))
done

But, that doesn't give you fixed width increments.

We can use printf to fix this
FILENO=1
for i in *.jpg ;
do
mv "$i" "file$(printf '%04d' $((FILENO))).jpg"
FILENO=$((FILENO + 1))
done

or even use bash parameter expansion to pad and truncate the value
FILENO=1
for i in *.jpg ;
do
RFNO=0000${FILENO}
mv "$i" "file${RFNO: -4}.jpg"
FILENO=$((FILENO + 1))
done

HTH

Cecil Westerhof

unread,
Aug 31, 2014, 4:47:16 PM8/31/14
to
Op Sunday 31 Aug 2014 21:32 CEST schreef Lew Pitcher:
I would use (I only use all caps for read-only variables:
declare -i fileNo=1

fileNo+=1

Is more clear and probably more efficient.

--
Cecil Westerhof
Senior Software Engineer
LinkedIn: http://www.linkedin.com/in/cecilwesterhof

Lew Pitcher

unread,
Aug 31, 2014, 6:55:30 PM8/31/14
to
On Sunday 31 August 2014 16:47, in alt.os.linux, "Cecil Westerhof"
<Ce...@decebal.nl> wrote:

> Op Sunday 31 Aug 2014 21:32 CEST schreef Lew Pitcher:
>
>> On Sunday 31 August 2014 15:12, in alt.os.linux, "Lew Pitcher"
>> <lew.p...@digitalfreehold.ca> wrote:
>>
>>> On Sunday 31 August 2014 14:54, in alt.os.linux, "Seamus Okafor"
>>> <Seamus...@is.invalid> wrote:
>>>
>>>> I want to move a series of files to numerical names, e.g.,
>>>> mv firstfile.jpg file001.jpg
>>>> mv secondfile.jpg file002.jpg
>>>> mv thirdfile.jpg file003.jpg
>>>> etc.
>>>>
>>>> I know the syntax for a for loop, e.g.,
>>>> $ for i in *.jpg do; mv $i $i_00#.jpg; done
>>>>
>>>> But, how do I increment the number to be 1, and then 2,
>>>> and then 3, etc.?
>>>
>>> using the $(()) syntax, you can increment a variable.
[snip]
> I would use (I only use all caps for read-only variables:
> declare -i fileNo=1
> …
> fileNo+=1
>
> Is more clear and probably more efficient.

Kewl! I learned something new

Thank you
:-)

Seamus Okafor

unread,
Aug 31, 2014, 7:22:45 PM8/31/14
to
Cecil Westerhof wrote, on Sun, 31 Aug 2014 22:47:16 +0200:

> declare -i fileNo=1
> …
> fileNo+=1

The other suggestions seemed too complicated to write off the cuff
every time I create an ls-script, so, I tried this simpler one:

$ for f in *.jpg; do declare -i fileNo=1; cp $f pic_$fileNo;fileNo+=1;done

But it failed to do anything (not even an error).

Trying to see what it did, I wrote:
$ for f in *.jpg; do declare -i fileNo=1; echo $f $fileNo;fileNo+=1;done
with the result that the fileNo didn't increment:

20140829_085929.jpg 1
20140829_085937.jpg 1
20140829_091353.jpg 1
20140829_091652.jpg 1
20140829_091700.jpg 1

Any idea why the fileNo didn't increment?


Cecil Westerhof

unread,
Aug 31, 2014, 8:08:27 PM8/31/14
to
Op Monday 1 Sep 2014 01:22 CEST schreef Seamus Okafor:
Try:
declare -i fileNo=1 ; for f in *.jpg ; do echo $f $fileNo ; fileNo+=1 ; done

William Unruh

unread,
Aug 31, 2014, 8:32:13 PM8/31/14
to
On 2014-08-31, Seamus Okafor <Seamus...@is.invalid> wrote:
> Cecil Westerhof wrote, on Sun, 31 Aug 2014 22:47:16 +0200:
>
>> declare -i fileNo=1
>> ???
>> fileNo+=1
>
> The other suggestions seemed too complicated to write off the cuff
> every time I create an ls-script, so, I tried this simpler one:
>
> $ for f in *.jpg; do declare -i fileNo=1; cp $f pic_$fileNo;fileNo+=1;done
>
> But it failed to do anything (not even an error).
>
> Trying to see what it did, I wrote:
> $ for f in *.jpg; do declare -i fileNo=1; echo $f $fileNo;fileNo+=1;done
> with the result that the fileNo didn't increment:
>
> 20140829_085929.jpg 1
> 20140829_085937.jpg 1
> 20140829_091353.jpg 1
> 20140829_091652.jpg 1
> 20140829_091700.jpg 1
>
> Any idea why the fileNo didn't increment?
>

Because you declare it to be equal to 1 every time the do loop repeats.
Get that declare outside the do loop.


>

William Unruh

unread,
Aug 31, 2014, 8:35:16 PM8/31/14
to
On 2014-08-31, Seamus Okafor <Seamus...@is.invalid> wrote:
Which number?
There is also the option
for ((i=1;i<100;i++))
do
...
done
But if you want both the filename and the number to be incremented at
each round, then one of the previous suggestions would probably be
better.

Seamus Okafor

unread,
Aug 31, 2014, 9:54:38 PM8/31/14
to
Cecil Westerhof wrote, on Mon, 01 Sep 2014 02:08:27 +0200:

> declare -i fileNo=1 ; for f in *.jpg ; do echo $f $fileNo ; fileNo+=1 ; done

Ooooh. Nice. Thanks!

Now I can just type that at the command line prompt whenever I want to
name files based on the numerical sequence!

$ declare -i fileNo=1 ; for f in *.jpg ; do echo $f $fileNo ; fileNo+=1 ; done
20140820_172639.jpg 1
20140820_172654.jpg 2
20140821_101548.jpg 3
20140821_153200.jpg 4
20140821_153203.jpg 5
20140821_204332.jpg 6
20140821_204333.jpg 7
20140822_091223.jpg 8
20140822_091227.jpg 9
20140822_091233.jpg 10
20140822_091237.jpg 11
20140822_091250.jpg 12

Seamus Okafor

unread,
Aug 31, 2014, 9:57:30 PM8/31/14
to
William Unruh wrote, on Mon, 01 Sep 2014 00:32:13 +0000:

> Get that declare outside the do loop.

As per you and Cecil, moving the declare outside the do loop works perfectly!

$ declare -i num=1;for f in *.jpg;do cp $f pic_$num;num+=1;done

Seamus Okafor

unread,
Aug 31, 2014, 9:58:18 PM8/31/14
to
William Unruh wrote, on Mon, 01 Sep 2014 00:35:16 +0000:

> Which number?

This is all I needed!
$ declare -i fileNo=1;for f in *.jpg;do cp $f pic_$fileNo;fileNo+=1;done

Jasen Betts

unread,
Sep 1, 2014, 2:52:20 AM9/1/14
to
On 2014-08-31, Seamus Okafor <Seamus...@is.invalid> wrote:
maybe this?

$ n=1 ; for i in *.jpg do; mv -i $i $((n++)).jpg; done



--
umop apisdn


--- news://freenews.netfront.net/ - complaints: ne...@netfront.net ---

Cecil Westerhof

unread,
Sep 1, 2014, 3:18:22 AM9/1/14
to
Op Monday 1 Sep 2014 03:54 CEST schreef Seamus Okafor:
You still need to do the renaming instead of the echo. ;-)

Seamus Okafor

unread,
Sep 1, 2014, 8:43:26 AM9/1/14
to
Cecil Westerhof wrote, on Mon, 01 Sep 2014 09:18:22 +0200:

> You still need to do the renaming instead of the echo. ;-)

The only problem I have so far with the command is that it
screws up the file sorting order:

$ declare -i i=1;for f in *.jpg;do cp $f foo_$i.jpg;i+=1;done

The result, unfortunately, sorts badly as:
foo_1.jpg
foo_10.jpg <== it goes from 1 and skips 2 through 9 & goes to 10
foo_11.jpg
...
foo_2.jpg <== in the middle of nowhere, 2 finally shows up
foo_20.jpg
foo_21.jpg
...

Is there a way to get it to sort correctly?

Seamus Okafor

unread,
Sep 1, 2014, 8:47:18 AM9/1/14
to
Jasen Betts wrote, on Mon, 01 Sep 2014 06:52:20 +0000:

> maybe this?
>
> $ n=1 ; for i in *.jpg do; mv -i $i $((n++)).jpg; done

$ n=1 ; for i in *.jpg do; mv -i $i $((n++)).jpg; done
bash: syntax error near unexpected token `mv'

$ n=1 ; for i in *.jpg;do mv -i $i $((n++)).jpg; done

That works great! Thanks.

The only problem is the sorting order is all screwed up,
where 1 is next to 10 and we don't see 2 until we get to
20, but other than the messed-up sort, it works great!

Cecil Westerhof

unread,
Sep 1, 2014, 9:10:32 AM9/1/14
to
Op Monday 1 Sep 2014 14:43 CEST schreef Seamus Okafor:
Look at the reply of Lew Pitcher.

blind Pete

unread,
Sep 1, 2014, 9:58:39 AM9/1/14
to
man rename

--
blind Pete
Sig goes here...

Ned Turnbull

unread,
Sep 1, 2014, 10:25:07 AM9/1/14
to
On Mon, 01 Sep 2014 23:58:39 +1000, blind Pete wrote:

> man rename

A funny thing happened when I cut and pasted the rename example
into the bash shell. It mysteriously disappeared!

That is, if I "type" the command, it shows up in the "history",
but, if I cut directly from the manpage example, and paste the
rename example into the bash shell, the command works, but, it
fails to show up in the history. I tried this many times, and
it happened *every* time I cut and pasted from the manpage, but
not when I typed it in. My conclusion is that there are "special"
hidden characters in the manpage that make it invisible to the
history command, but, that would be a bug in the history command
that was unknown until this point.

rename 'y/A-Z/a-z/' *

Try this:
1. Create a dummy directory with upper-and-lower case filenames.
2. Type the command above, and hit enter.
3. Then, type "history", and you'll see your command listed.
4. Then, run a "man rename" and cut/paste the rename example.
5. When you run history, you won't see that command.
6. The invisible command can be made visible by removing the
white space in front.

But, the amazing thing is that the command worked, but, by
prepending it with the whitespace (or whatever) from the manpage,
you made it invisible to the history command.

Amazing.

Jasen Betts

unread,
Sep 1, 2014, 9:19:01 AM9/1/14
to
easiest is chop its head off

i=1001 ; for f in *.jpg;do cp $f foo_${i:2}.jpg;((i+=1));done

else if you want to be safe with larger counts you could use printf,
I guess.

Seamus Okafor

unread,
Sep 1, 2014, 1:13:10 PM9/1/14
to
Jasen Betts wrote, on Mon, 01 Sep 2014 13:19:01 +0000:

> i=1001 ; for f in *.jpg;do cp $f foo_${i:2}.jpg;((i+=1));done

That worked nicely.
Now I have to figure out /how/ it worked!

$ i=1001;for f in *.jpg;do cp $f foo_${i:2}.jpg;((i+=1));done
Result:
foo_01.jpg
foo_02.jpg
foo_03.jpg
foo_04.jpg
foo_05.jpg
foo_06.jpg
foo_07.jpg
foo_08.jpg
foo_09.jpg
foo_10.jpg
foo_11.jpg
foo_12.jpg
foo_13.jpg
foo_14.jpg
foo_15.jpg
foo_16.jpg
foo_17.jpg
foo_18.jpg
foo_19.jpg
foo_20.jpg
foo_21.jpg
foo_22.jpg
foo_23.jpg
foo_24.jpg
foo_25.jpg
etc.

Jasen Betts

unread,
Sep 2, 2014, 8:31:47 AM9/2/14
to
Which one? "rename" from util-linux or from Larry Wall?

Jasen Betts

unread,
Sep 2, 2014, 8:35:35 AM9/2/14
to
On 2014-09-01, Seamus Okafor <Seamus...@is.invalid> wrote:
> Jasen Betts wrote, on Mon, 01 Sep 2014 13:19:01 +0000:
>
>> i=1001 ; for f in *.jpg;do cp $f foo_${i:2}.jpg;((i+=1));done
>
> That worked nicely.
> Now I have to figure out /how/ it worked!
>

i is an ordinary shell variable it starts starts at 1001

${i:2} takes drops the first 2 characters of i leaing "01"

hence my "chop of it's head" remark

((i+=1)) adds one to i

I expect you can figure the rest out

William Unruh

unread,
Sep 2, 2014, 1:43:50 PM9/2/14
to
On 2014-09-02, Jasen Betts <ja...@xnet.co.nz> wrote:
> On 2014-09-01, blind Pete <0123...@gmail.com> wrote:
>> Seamus Okafor wrote:
>>
>>> Cecil Westerhof wrote, on Mon, 01 Sep 2014 09:18:22 +0200:
>>>
>>>> You still need to do the renaming instead of the echo. ;-)
>>>
>>> The only problem I have so far with the command is that it
>>> screws up the file sorting order:
>>>
>>> $ declare -i i=1;for f in *.jpg;do cp $f foo_$i.jpg;i+=1;done

declare -i i=1; for f in *.jpg; do cp $f foo_`printf "%.04d" $i`.jpg;
i+=1; done
The print will print the number with leading 0 so that the length is 4
digits.

foo_0001.jpg
foo_0002.jpg
...
foo_0179.jpg
...
foo_5923.jpg
...
(If you have more than 9999 files you will start having trouble.)
The sort on this will behave properly.
0 new messages