Removing leading zeros before passing a shell variable to another command
BashStripBash 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.