7

How does one iterate a string with the form "[A-Z][0-9]*" Or for example: "A000001"?

After receiving the variable I split:

current_=$(mysql -h"$mysqlhost" -u"$mysqluser" -p"$PASS" "$DBNAME" -se "SELECT current_ FROM $GLOBALDB;")
current_number=$(echo $current_ | grep -oh "[0-9]*")
current_letter=$(echo $current_ | grep -oh "[A-Z]*")

However when I try to add 1:

# add 1 & keep all leading zeros "000001"
next_number=$(printf %06d $(($current_number + 1)))

It counts to "000009" and rolls back over to "000000".

And I join as follows:

next_=$(echo "$current_letter$next_number")

And in regards to the Letter iteration I was thinking of using an Associated Array? Or brace expansion {A..Z}, but that is a whole different question.

13

In bash, numbers with leading zeros are considered as octal.

You can use:

next_number=$(printf %06d "$((10#$current_number + 1))")

To tell bash to consider it as decimal.

See also:

$ printf 'A%06d\n' {5..12}
A000005
A000006
A000007
A000008
A000009
A000010
A000011
A000012

Or:

$ printf '%s\n' {A..C}{00008..00012}
A00008
A00009
A00010
A00011
A00012
B00008
B00009
B00010
B00011
B00012
C00008
C00009
C00010
C00011
C00012

Or:

$ seq -f A%06g 5 12
A000005
A000006
A000007
A000008
A000009
A000010
A000011
A000012
5

Perl to the rescue:

perl -E '$x = "A001"; say $x++ for 1 .. 1002' 

Output:

A001
A002
A003
A004
A005
A006
A007
A008
A009
A010
A011
...
A996
A997
A998
A999
B000
B001
B002

The ++ operator handles both letters and numbers.

  • I know perl is included in Ubuntu, but is it a bash builtin? I have exactly 0 experience writing in perl. This clearly works. – jmunsch Nov 18 '14 at 16:09
  • @jmunsch: Perl is not a part of bash. It's a programming language of its own. – choroba Nov 18 '14 at 16:11
  • I did some searching. It looks as though Perl is in a lot of distros? And adding a dependency on Perl is not a problem? Thanks for your answer. – jmunsch Nov 18 '14 at 16:14
  • 1
    @jmunsch: Perl should be there on any Linux. You can find it or install it on most *ix systems, too, as well as MS Windows. – choroba Nov 18 '14 at 16:16
  • Would this be a better perl answer? export current_ && perl -E 'say $ENV{current_}++' because I wasn't able to import the variable from bash to perl? – jmunsch Nov 18 '14 at 17:35
1

For historical reasons, numerical operations in Bourne/POSIX-style shells parse integer constants with a leading 0 digit as octal rather than decimal. So 08 in an arithmetic operating is a syntax error, the successor of 07 is 8, and 010 is equivalent to 8.

You can use normal arithmetic and then pad the numbers when printing them with the printf builtin.

next_number=$(($current_number + 1))
printf -v padded_next_number %06d $next_number

The -v option to printf is bash-specific; the POSIX way is

next_number=$(($current_number + 1))
padded_next_number=$(printf %06d $next_number)

Here's another approach which was useful in historical systems that didn't have a printf command, and can still be useful for performance in the few shells that don't have printf as a builtin. Instead of counting from 1, count from 1000001. That way, your numbers never have to have leading zeroes. When you use the number, strip off the leading 1 digit.

number=1000000
while … ; do
  number=$((number+1))
  echo "${number#1}"
  …
done

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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