Removing leading zeros before passing a shell variable to another command

BashStrip

Bash Problem Overview


It turns out that iptables doesn't handle leading zeros too well. As $machinenumber that is used has to have a leading zero in it for other purposes, the idea is simply to create a new variable ($nozero) based on $machinenumber, where leading zeros are stripped away.

$machinenumber is a two-digit number between 01 and 24. Currently it's 09

$machinetype is 74 for now and hasn't caused any problems before.

What I have so far is:

nozero = (echo $machinenumber | sed 's/^0*//')
iptables -t nat -I POSTROUTING -s 10.($machinetype).($nozero).0/24 -j MASQUERADE

While I believe I'm on the right track, the code results in:

ERROR - Unknown string operation

Bash Solutions


Solution 1 - Bash

You don't need to use sed or another external utility. Here are a couple of ways Bash can strip the leading zeros for you.

iptables -t nat -I POSTROUTING -s "10.$machinetype.$((10#$machinenumber)).0/24" -j MASQUERADE

The $(()) sets up an arithmetic context and the 10# converts the number from base 10 to base 10 causing any leading zeros to be dropped.

shopt -s extglob
iptables -t nat -I POSTROUTING -s "10.$machinetype.${machinenumber##+(0)}.0/24" -j MASQUERADE

When extglob is turned on, the parameter expansion shown removes all leading zeros. Unfortunately, if the original value is 0, the result is a null string.

Solution 2 - Bash

No, you make all (alomost all) correct. You just must:

  • remove spaces around =
  • use $() or backticks instead of ()

That would be correct:

 nozero=$(echo $machinenumber | sed 's/^0*//')

Also you must use variables without () around them. You can add "" if you want:

iptables -t nat -I POSTROUTING -s "10.$machinetype.$nozero.0/24" -j MASQUERADE

And of course variables here are not necessary. You can say simply:

iptables -t nat -I POSTROUTING -s "10.$(echo $machinenumber | sed 's/^0*//').$nozero.0/24" -j MASQUERADE

Solution 3 - Bash

you can also do

machinenumber=$(expr $machinenumber + 0)

Solution 4 - Bash

I can't comment as I don't have sufficient reputation, but I would suggest you accept Dennis's answer (which is really quite neat)

Firstly, I don't think that your answer is valid bash. In my install I get:

> machinetype=74
> machinenumber=05
> iptables -t nat -I POSTROUTING -s 10.($machinetype).($machinenumber + 0).0/24 -j MASQUERADE
-bash: syntax error near unexpected token `('
> echo 10.($machinetype).($machinenumber + 0).0/24
-bash: syntax error near unexpected token `('

If I quote it I get:

> echo "($machinetype).($machinenumber + 0)"
(74).(05 + 0)

I'm assuming you mean:

> echo 10.$(($machinetype)).$(($machinenumber + 0)).0/24
10.74.5.0/24

But, of course it's still a bad solution because of octal:

> machinenumber=09
> echo 10.$(($machinetype)).$(($machinenumber + 0)).0/24
-bash: 09: value too great for base (error token is "09")

I assume that your numbers aren't 08 or 09 at the moment.

Here's Dennis's:

> echo $((10#09))
9
> echo $((10#00))
0
> echo $((10#00005))
5
> echo $((10#))
0

Admittedly, that last one might be an input validation problem for someone.

The sed solution has the problem of:

> echo "0" | sed 's/^0*//'

>

Solution 5 - Bash

nozero=$(echo $machinenumber | sed 's/^0*//')

Try without the spaces around = and with an additional $ sign.

Solution 6 - Bash

A pure bash solution:

> N=0001023450 
> [[ $N =~ "0*(.*)" ]] && N=${BASH_REMATCH[1]}
> echo $N 
1023450

Solution 7 - Bash

Using sed:

echo 000498 | sed "s/^0*\([1-9]\)/\1/;s/^0*$/0/"
498
echo 000 | sed "s/^0*\([1-9]\)/\1/;s/^0*$/0/"
0

Solution 8 - Bash

I do it by using

awk '{print $1 + 0}'

I like this better than the sed approach as it still works with numbers like 0, 000, and 001.

So in your example I would replace

nozero=$(echo $machinenumber | sed 's/^0*//')

with

nozero=$(echo $machinenumber | awk '{print $1 + 0}' )

Solution 9 - Bash

I also can't comment or vote up yet, but the Duncan Irvine answer is the best.

I'd like to add a note about portability. The $((10#0009)) syntax is not portable. It works in bash and ksh, but not in dash:

$ echo $((10#09))
dash: 1: arithmetic expression: expecting EOF: "10#09"

$ dpkg -s dash | grep -i version
Version: 0.5.7-2ubuntu2

If portability is important to you, use the sed answer.

Solution 10 - Bash

If you are using bash, this looks like the simplest:

nozero=$(bc<<<$machinenumber)

Solution 11 - Bash

Add the number with 0, it will remove leading zeroes

eg: expr 00010 + 0 #becomes 10

OR

num=00076
num=${num##+(0)}
echo $num

Solution 12 - Bash

I would say you are very close. I do not see a requirement stated for bash, but your nonzero logic is flawed.

nonzero=`echo $machinenumber + 0 | bc`
iptables -t nat -I POSTROUTING -s 10.$machinetype.$nozero.0/24 -j MASQUERADE

Adding 0 is a common method for changing a string number into a non-padded integer. bc is a basic calculator. I use this method for removing space and zero padding from numbers all the time.

While I am not expert in iptables syntax, I am pretty sure the parenthesis are not necessary. Since I already have non-word characters bordering both variables, I do not need special enclosures around them. Word characters are;

[a-zA-z0-9_]

Using this solution, you do not lose zero as a potential value, and should be portable across all shells.

Solution 13 - Bash

I had to revisit this code the other day due to some unrelated stuff, and due to compatibility with some other software that reads the same script, i found it a lot easiest to rewrite it into this, which should still be valid bash:

iptables -t nat -I POSTROUTING -s 10.($machinetype).($machinenumber + 0).0/24 -j MASQUERADE

Basically, adding 0 forces it to be interpreted as an integer, hence automatically stripping the leading zeros

Solution 14 - Bash

If you don't have sed or awk or perl then you could use cut and grep

mn="$machinenumber"
while echo "$mn"|grep -q '^0';do mn=$(echo "$mn"|cut -c2-);done
iptables -t nat -I POSTROUTING -s 10.($machinetype).($mn).0/24 -j MASQUERADE

Works with both bash and dash shells.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJarmundView Question on Stackoverflow
Solution 1 - BashDennis WilliamsonView Answer on Stackoverflow
Solution 2 - BashIgor ChubinView Answer on Stackoverflow
Solution 3 - BashpizzaView Answer on Stackoverflow
Solution 4 - BashDuncan IrvineView Answer on Stackoverflow
Solution 5 - BashBalintView Answer on Stackoverflow
Solution 6 - Bashuser136623View Answer on Stackoverflow
Solution 7 - BashJahidView Answer on Stackoverflow
Solution 8 - BashCullen Fluffy JenningsView Answer on Stackoverflow
Solution 9 - BashAurelio JargasView Answer on Stackoverflow
Solution 10 - BashCykerView Answer on Stackoverflow
Solution 11 - BashAashutosh KumarView Answer on Stackoverflow
Solution 12 - BashDaniel ListonView Answer on Stackoverflow
Solution 13 - BashJarmundView Answer on Stackoverflow
Solution 14 - BashAlexx RocheView Answer on Stackoverflow