Make sure int variable is 2 digits long, else add 0 in front to make it 2 digits long - linux

How do I check a int variable ($inputNo) to see if it’s 2 or more decimal digits long?
Example:
inputNo="5"
Should be changed to: 05
inputNo="102"
Should be left alone: 102
I thought about using wc and if statements, but wc -m doesn’t seems to give the actual characters passed into wc, as wc always seems to give +1 to the characters that is given.
But I don’t know how to add a 0 in front of the current input number.

You can use the bash-builtin printf with the -v option to write it to a variable rather than print it to standard output:
pax> inputNo=5 ; printf -v inputNo "%02d" $inputNo ; echo $inputNo
05
pax> inputNo=102 ; printf -v inputNo "%02d" $inputNo ; echo $inputNo
102
You'll want to make sure it's numeric first otherwise the conversion will fail. If you want to be able to pad any string out to two or more characters, you can also use:
while [[ ${#inputNo} -lt 2 ]] ; do
inputNo="0${inputNo}"
done
which is basically a while loop that prefixes your string with "0" until the length is greater than or equal to two.
Note that this can also be done in bash by prefixing the number with two zeroes then simply getting the last two characters of that string, checking first that it's not already at least the desired size:
if [[ ${#inputNo} -lt 2 ]] ; then
inputNo="00${inputNo}"
inputNo="${inputNo: -2}"
fi
The difference is probably not too great for a two-digit number but you may find the latter solution is better if you need larger widths.
If you're using a shell other than bash (unlikely, based on your tags), you'll need to find the equivalents, or revert to using external processes to do the work, something like:
while [[ $(echo -n ${inputNo} | wc -c) -lt 2 ]] ; do
inputNo="0${inputNo}"
done
This does basically what you were thinking off in your question but note the use of -n in the echo command to prevent the trailing newline (which was almost certainly causing your off-by-one error).
But, as stated, this is a fall-back position. If you're using bash, the earlier suggestions of mine are probably best.

For general-purpose padding whether the string is numeric or not
No need for piping echo into wc or using a while loop.
In Bash, you can get the length of a string like this: ${#inputNo}.
And since you can do substrings, you can do this instead:
if [[ ${#input} < 2 ]]
then
inputNo="00${inputNo}"
inputNo="${inputNo: -2}"
fi

You can use http://bash-hackers.org/wiki/doku.php/commands/builtin/printf, an example from there:
the_mac="0:13:ce:7:7a:ad"
# lowercase hex digits
the_mac="$(printf "%02x:%02x:%02x:%02x:%02x:%02x" 0x${the_mac//:/ 0x})"
# or the uppercase-digits variant
the_mac="$(printf "%02X:%02X:%02X:%02X:%02X:%02X" 0x${the_mac//:/ 0x})"

Related

How to match substring with special characters in linux shell?

I wanted to check if a file contains a string in bash. I have done so for regular string characters. However, i need to check a new string which contains double quote ".
Say the string is:
PARAMETER_XYZ="no"
I tried this, but does not work:
ABC=$(cat /etc/file)
if [[ $ABC = *"PARAMETER_XYZ=\"no\""*]] ; then
exit 0
fi
Any suggestions?
grep is the program of choice to look for the presence of strings.
if grep 'PARAMETER_XYZ="no"' /etc/file > /dev/null then
exit 0
fi
You can also still do it with [[ if you really want:
ABC=$(cat /etc/file)
if [[ $ABC = *'PARAMETER_XYZ="no"'* ]] ; then
exit 0
fi
However, if that's actually a config file you're trying to parse, there are better solutions that are less fragile than looking for an exact string. That file looks like it might even be a shell variables file, in which case you could just source it and then check $PARAMETER_XYZ directly.

How can I get the length of each output line of grep

I am very new to bash scripting.
I have a network trace file I want to parse. Part of the trace file is (two packets):
[continues...]
+---------+---------------+----------+
05:00:00,727,744 ETHER
|0
|00|03|a0|09|5c|1c|00|10|07|df|a4|20|08|00|45|00|00|38|e7|55|
+---------+---------------+----------+
05:00:00,727,751 ETHER
|0
|00|03|a0|09|5c|1c|00|10|07|df|a4|20|08|00|45|00|00|38|e7|56|00|00|3a|01|
[continues...]
For each packet, I want to print the time stamp, and the length of the packet (the hex values coming on the next line after |0 header) so the output will look like:
05:00:00.727744 20 bytes
05:00:00.727751 24 bytes
I can get the line with time stamp and the packets separately using grep in bash:
times=$(grep '..\:..\:' $fileName)
packets=$(grep '..|..|' $fileName)
But I can't work with the separate output lines after that. The whole result is concatenated in the two variables "times" and "packets". How can I get the length of each packet?
P.S. a good reference that really explains how to do bash programming, rather than just doing examples would be appreciated.
Okay, with plain old shell...
You can get the length of the line like this:
line="|00|03|a0|09|5c|1c|00|10|07|df|a4|20|08|00|45|00|00|38|e7|55|"
wc -c<<<$line
62
There are sixty two characters in that line. Think of each character as |00 where 00 can be any digit. In that case, there's an extra | on the end. Plus, the wc -c includes the NL on the end.
So, if we take the value of wc -c, and subtract 2, we get 60. If we divide that by 3, we get 20 which is the number of characters.
Okay, now we need a little loop, figure out the various lines, and then parse them:
#! /bin/bash
while read line
do
if [[ $line =~ ^[[:digit:]]{2} ]]
then
echo -n "${line% *}"
elif [[ $line =~ ^\|[[:digit:]]{2} ]]
then
length=$(wc -c<<<$line)
((length-=2))
((length=length/3))
echo "$length bytes"
fi
done < test.txt
There a PURE BASH solution to your problems!
You're a beginning Bash programmer, and you have no idea what's going on...
Let's take this one step at a time:
A common way to loop through a file in BASH is using a while read loop. This combines the while with a read:
while read line
do
echo "My line is '$line'"
done < test.txt
Each line in test.txt is being read into the $line shell variable.
Let's take the next one:
if [[ $line =~ ^[[:digit:]]{2} ]]
This is an if statement. Always use the [[ ... ]] brackets because they fix issues with the shell interpolating stuff. Plus, they have a bit more power.
The =~ is a regular expression match. The [[:digit:]] matches any digit. The ^ anchors the regular expression to the beginning of the line, and {2} means I want exactly two of these. This says if I match a line that starts with two digits (which is your timestamp line), execute this if clause.
${line% *} is a pattern filter. The % says to match the (glob) smallest glob pattern to the right and filter it from my $line variable. I use this to remove the ETHER from my line. The -n tells echo not to do a NL.
Let's take my elif which is an else if clause.
elif [[ $line =~ ^\|[[:digit:]]{2} ]]
Again, I am matching a regular expression. This regular expression starts with (The ^) a |. I have to put a backslash in front because | is a magical regular expression character and \ kills the magic. It's now just a pipe. Then, that's followed by two digits. Note this skips |0 but catches |00.
Now, we have to do some calculations:
length=$(wc -c<<<$line)
The $(...) say to execute the enclosed command and resubstitute it back in the line. The wc -c counts the characters and <<<$line is what we're counting. This gave us 62 characters. We have to subtract 2, then divide by 3. That's the next two lines:
((length-=2))
((length/=3))
The ((...)) allows me to do integer based math. The first subtracts 2 from $length and the next divides it by 3. Now, I can echo this out:
echo "$length bytes"
And that's our pure Bash answer to this question.
You really don't want to do such things with your shell.
You want to write a real parser that understands the format to output the needed informations.
For a quick and dirty hack you can do something like that:
perl -wne 'print "$& " if /^\d\S*/; print split(/\|/)-2, " bytes\n" if /^\|..\|/'

Line from bash command output stored in variable as string

I'm trying to find a solution to a problem analog to this one:
#command_A
A_output_Line_1
A_output_Line_2
A_output_Line_3
#command_B
B_output_Line_1
B_output_Line_2
Now I need to compare A_output_Line_2 and B_output_Line_1 and echo "Correct" if they are equal and "Not Correct" otherwise.
I guess the easiest way to do this is to copy a line of output in some variable and then after executing the two commands, simply compare the variables and echo something.
This I need to implement in a bash script and any information on how to get certain line of output stored in a variable would help me put the pieces together.
Also, it would be cool if anyone can tell me not only how to copy/store a line, but probably just a word or sequence like : line 1, bytes 4-12, stored like string in a variable.
I am not a complete beginner but also not anywhere near advanced linux bash user. Thanks to any help in advance and sorry for bad english!
An easier way might be to use diff, no?
Something like:
command_A > command_A.output
command_B > command_B.output
diff command_A.output command_B.output
This will work for comparing multiple strings.
But, since you want to know about single lines (and words in the lines) here are some pointers:
# first line of output of command_A
command_A | head -n 1
The -n 1 option says only to use the first line (default is 10 I think)
# second line of output of command_A
command_A | head -n 2 | tail -n 1
that will take the first two lines of the output of command_A and then the last of those two lines. Happy times :)
You can now store this information in a variable:
export output_A=`command_A | head -n 2 | tail -n 1`
export output_B=`command_B | head -n 1`
And then compare it:
if [ "$output_A" == "$output_B" ]; then echo 'Correct'; else echo 'Not Correct'; fi
To just get parts of a string, try looking into cut or (for more powerful stuff) sed and awk.
Also, just learing a good general purpose scripting language like python or ruby (even perl) can go a long way with this kind of problem.
Use the IFS (internal field separator) to separate on newlines and store the outputs in an array.
#!/bin/bash
IFS='
'
array_a=( $(./a.sh) )
array_b=( $(./b.sh) )
if [ "${array_a[1]}" = "${array_b[0]}" ]; then
echo "CORRECT"
else
echo "INCORRECT"
fi

For i in loop with printf error

My script:
#!/bin/sh
for i in {1..3}
do
cp dummy.dat dummy/dummy.`printf "%04d%s_shp" ${i}`
done
and the
error: printf: 9: {1..3}: expected numeric value
If I type:
for i in 0 2 3
The script is working.
What is wrong with my script on the top? Or anyone a solution?
Your /bin/sh doesn't support {1..3} which is a bash extension. You can either:
Use #!/bin/bash to ensure the script is always run with bash.
Use $(seq 1 3) which is a POSIX-compliant replacement which will supposedly work with all shells.
printf "%04d%s_shp" ${i}
^ ^
1 2
printf expects two values.
The %04d means it wants a number (which will be given 4 spaces and padded with leading zeros), the %s means it expects to get a string value. You are only supplying one value, i, to printf
Only a guess, but did you mean perhaps just:
printf "%04d_shp" ${i}
i.e., without the s% ?
The literal {1..3} is not numeric. It has numbers in it, but the shell doesn't (necessarily) interpret it as a range in a for; you have to write it out in full (or use a while loop and some expression calculation).

Bash script to copy numbered files in reverse order

I have a sequence of files:
image001.jpg
image002.jpg
image003.jpg
Can you help me with a bash script that copies the images in reverse order so that the final result is:
image001.jpg
image002.jpg
image003.jpg
image004.jpg <-- copy of image003.jpg
image005.jpg <-- copy of image002.jpg
image006.jpg <-- copy of image001.jpg
The text in parentheses is not part of the file name.
Why do I need it? I am creating video from a sequence of images and would like the video to play "forwards" and then "backwards" (looping the resulting video).
You can use printf to print a number with leading 0s.
$ printf '%03d\n' 1
001
$ printf '%03d\n' 2
002
$ printf '%03d\n' 3
003
Throwing that into a for loop yields:
MAX=6
for ((i=1; i<=MAX; i++)); do
cp $(printf 'image%03d.jpg' $i) $(printf 'image%03d.jpg' $((MAX-i+1)))
done
I think that I'd use an array for this... that way, you don't have to hard code a value for $MAX.
image=( image*.jpg )
MAX=${#image[*]}
for i in ${image[*]}
do
num=${i:5:3} # grab the digits
compliment=$(printf '%03d' $(echo $MAX-$num | bc))
ln $i copy_of_image$compliment.jpg
done
I used 'bc' for arithmetic because bash interprets leading 0s as an indicator that the number is octal, and the parameter expansion in bash isn't powerful enough to strip them without jumping through hoops. I could have done that in sed, but as long as I was calling something outside of bash, it made just as much sense to do the arithmetic directly.
I suppose that Kuegelman's script could have done something like this:
MAX=(ls image*.jpg | wc -l)
That script has bigger problems though, because it's overwriting half of the images:
cp image001.jpg image006.jpg # wait wait!!! what happened to image006.jpg???
Also, once you get above 007, you run into the octal problem.

Resources