Bash for loop on variable numbers - linux

I have a situation where I have large number of numbered variables. I want to evaluate each variable and set variable to a specific string if the condition is matched.
#!/bin/bash
var1=""
var2="1233123213"
var3="22332323222324242"
var4=""
var5=""
for i in 1 2 3 4 5
do
if [ -z "$var{$}i" ]
then
var{$}i="None"
fi
echo "var{$}i \r"
done
but the problem is when I run the script I get following.
{1} \r
{2} \r
{3} \r
{4} \r
{5} \r
How I can fix this.

Use indirect variable expansion in bash with syntax {!var}.
From the man bash page,
If the first character of parameter is an exclamation point (!), a level of variable indirection is introduced. Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself. This is known as indirect expansion. The exclamation point must immediately follow the left brace in order to introduce indirection.
Modify your code to something like below,
for i in 1 2 3 4 5
do
var="var$i"
[ -z "${!var}" ] && declare "var$i"="none"
done
printf "var1=%s\n" "$var1"
printf "var2=%s\n" "$var2"
printf "var3=%s\n" "$var3"
printf "var4=%s\n" "$var4"
printf "var5=%s\n" "$var5"
The syntax "${!var}" in this case evaluates the value of the variable within the string var which is var1, var2, var3... and the declare syntax sets the variable value at run-time, only for those variables that are empty.
Now on printing those variables produces,
var1=none
var2=1233123213
var3=22332323222324242
var4=none
var5=none

Indirect assignment will work here, but in this specific case arrays seem like a good fit :
#!/bin/bash
declare -a var=()
var+=("")
var+=(1233123213)
var+=(22332323222324242)
var+=("")
var+=("")
for i in "${!var[#]}"
do
[[ "${var[$i]}" ]] || var[$i]="None"
echo "Index: $i - Value: ${var[$i]}"
done

Consider using an array instead of numbered variables:
#!/bin/bash
var[1]=""
var[2]="1233123213"
var[3]="22332323222324242"
var[4]=""
var[5]=""
for i in 1 2 3 4 5
do
if [ -z "${var[i]}" ]
then
var[i]="None"
fi
echo "${var[i]} \r"
done

Related

How to fix error "56949f: value too great for base (error token is "56949f")"

given code:
rosepine=("191724" "1f1d2e" "26233a" "6e6a86" "908caa" "e0def4" "eb6f92" "f6c177" "ebbcba" "31748f" "9ccfd8" "c4a7e7" "21202e" "403d52" "524f67")
rosepinemoon=("232136" "2a273f" "393552" "6e6a86" "908caa" "e0def4" "eb6f92" "f6c177" "ea9a97" "3e8fb0" "9ccfd8" "c4a7e7" "2a283e" "44415a" "56526e")
rosepinedawn=("faf4ed" "fffaf3" "f2e9e1" "9893a5" "797593" "575279" "b4637a" "ea9d34" "d7827e" "286983" "56949f" "907aa9" "f4ede8" "dfdad9" "cecacd")
base=0
surface=1
overlay=2
muted=3
subtle=4
text=5
love=6
gold=7
rose=8
pine=9
foam=10
iris=11
hltlow=12
hltmed=13
hlthigh=14
#test colours
show_colour() {
perl -e 'foreach $a(#ARGV){print "\e[48:2::".join(":",unpack("C*",pack("H*",$a)))."m \e[49m "};print "\n"' "$#"
}
theme="rosepinedawn" #
basec="text"
outlinec="subtle"
wheelc="foam"
xbc="'#$[$theme[$[$basec]]]'"
xoc="'#$[$theme[$[$outlinec]]]'"
xwc="'#$[$theme[$[$wheelc]]]'"
echo $xwc
error comes out as
line 36: 56949f: value too great for base (error token is "56949f")
I cant fix the formatting
I expected the output
#56949f
I understand that the base must be forced to 10 but i dont know where to specify
The $[expression] syntax is a very old style of arithmetic expression
which is now superseded by $(( expression )). The expression is
evaluated as a numeric value then 56949f will cause an error.
I'm afraid you might be confusing the arithmetic expression with
indirect variable references. Then you may want to say instead:
declare -n theme="rosepinedawn"
basec="text"
outlinec="subtle"
wheelc="foam"
xbc="#${theme[${!basec}]}"
xoc="#${theme[${!outlinec}]}"
xwc="#${theme[${!wheelc}]}"
echo "$xbc"
echo "$xoc"
echo "$xwc"
Output:
#575279
#797593
#56949f
Then why the OP's first two assignments xoc="'#$[$theme[$[$outlinec]]]'" and
xwc="'#$[$theme[$[$wheelc]]]'" look to work?
The Arithmetic Expansion section of bash manual says:
$((expression))
...
The expression is treated as if it were within double quotes
Let's see how xoc="'#$[$theme[$[$outlinec]]]'" is expanded.
echo "$theme[$[$outlinec]]"
=> rosepinedawn[5]
Then
echo "#$(( "$theme[$[$basec]]" ))" # same as "#$[ $theme[$[$basec]] ]"
=> echo "#$(( rosepinedawn[5] ))" # same as "#$[ rosepinedawn[5] ]"
=> #575279
As we do not have to put $ in front of the variable name in the arithmetic
expansion, rosepinedawn[5] evaluates to 575279.
[Edit]
There are several methods of indirect referencing of variables in bash,
meaning the case that a variable holds the name of another vaiable and
you want to refer to the latter variable using the former variable.
${!name}
This refers to the variable which name is stored in the variable $name.
Example:
dog="bow wow"
cat="meow"
for animal in "dog" "cat"; do
echo "$animal:" "${!animal}"
done
=>
dog: bow wow
cat: meow
where the variable $animal is assigned to dog or cat in the loop
and ${!animal} expands to the values of $dog or $cat.
Please note the ! syntax is not applicable for array variables because
the ${!name[#]} expression is reserved for the different purpose (it expands
to the keys of the array).
As a side note, we would use an associative array in most cases
instead of the indirect referencing mentioned above:
declare -A cry # create an associative array "cry"
cry["dog"]="bow wow" # associate "dog" with its cry
cry["cat"]="meow" # similar as above
for animal in "dog" "cat"; do
echo "$animal:" "${cry[$animal]}"
done
=>
dog: bow wow
cat: meow
declare -n nameref=name
This is another approach to indirectly refer to the value of the variable
including array variables. In the delcaration above, nameref
works as something like an alias or a reference to name.
You can dynamically change the value of nameref by using declare
to switch the referencing.
Example:
declare -a eng=("Sun" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat")
declare -a ger=("So" "Mo" "Di" "Mi" "Do" "Fr" "Sa")
declare -n week="eng"
echo "${week[2]}" # yields "Tue"
declare -n week="ger"
echo "${week[2]}" # yields "Di"
# you can also make a loop
for lang in "eng" "ger"; do
declare -n week="$lang"
echo "${week[3]}" # yields "Wed" and "Mi"
done

Iterate variables in a file to check for a particular value in bash

Below is my requirement. I have a text file that has following content
File name - abc.txt
Content -
apple=0
mango=1
strawberry=10
I need to kick off the subsequent process only if any of the above stated variable has non zero values.
In this case, As two variables have values 1 and 10 respectively, I need to update an indicator - SKIP INDICATOR=N
If all variables have 0 as value, I need to update SKIP INDICATOR=Y
How to achieve this functionality in Linux. Kindly advise.
with very simple greps :
if [ $(grep '=' your_file | grep -v '=0') ]
then
echo "non zero values detected"
SKIP_INDICATOR=N
else
echo "all are zeroes"
SKIP_INDICATOR=Y
fi
Just note that this is a quick and dirty solution and it would NOT work properly if you have for example a=01 or a= 0 (eg with space)
Try:
grep -q '=0*[1-9]' textfile && skip_indicator=N || skip_indicator=Y
=0*[1-9] matches an '=' character followed by zero or more '0' characters followed by a digit in the range 1 to 9.
See Correct Bash and shell script variable capitalization for an explanation of why I changed SKIP_INDICATOR to skip_indicator.
#!/bin/bash
flag=`awk -F'=' '$NF!="0"{print;exit}' input`
if [ ! -z $flag ] ; then
SKIP_INDICATOR=N
echo "some variable value is different from 0. do something"
else
SKIP_INDICATOR=Y
echo "all variables have 0 as value. do another thing."
fi
exit 0

Understand the use of braces and parenthesis in if

In some of the code I was going through I found that if was using braces {} for someplace and parenthesis (()) for some other. Can someone tell me the exact meaning and where to use which one?
if [ "$1" = "--help" ]
if (( $# != 3 ))
The bracket [ is a built-in command of the shell; you can also call it as test:
if [ a = b ]
then ...
equals:
if test a = b
then ...
The syntax of the test command is rather text-oriented (see the bash man page for details at chapter CONDITIONAL EXPRESSIONS).
The braces {…} are shell syntax and used for grouping commands (without creating a subshell):
{ date; ls; echo $$; } > 1>&2
This will execute date, ls, and echo $$ and redirect all their output to stderr.
The parenthesis (…) are shell-syntax and used for creating a subshell:
(date; ls; echo $$) > 1>&2
Like above but the PID ($$) given out is that of the subshell.
The difference between grouping and subshell is delicate (and out of scope here).
The doubled brackets [[…]] are shell syntax but otherwise behave like the single bracket [ command. The only difference is for using < etc. for string comparison and locale support.
The doubled parentheses ((…)) are equivalent to using the let builtin shell command. They basically allow number-oriented expressions to be evaluated (ARITHMETIC EVALUATION). < and > sort numerically (instead of lexicographically) etc. Also, in some constructs like for ((i=0; i<10; i++)); do echo "$i"; done they are used as a fixed syntax.
Dollar-parenthesis $(…) result in the output of the command they enclose:
echo "$(date)" # a complicated way to execute date
Dollar-brackets $[…] are deprecated and should be replaced by dollar-double-parentheses.
Dollar-double-parentheses $((…)) result in the value of the numerical expression they enclose:
echo "$((4 + 3 * 2))" # should print 10
Dollar-braces ${…} result in the variable expansion they enclose. In the simplest case this is just a variable name, then they evaluate to the variable value:
a=foo
echo "${a}" # prints foo
This can (and often is) abbreviated by stripping the braces: $a
But it also can be more complex like ${a:-"today is $(date)"}. See Parameter Expansion in the bash man page for details.
Redirection-parenthesis <(…) and >(…) create a subprocess, a file descriptor its output/input is associated with, and a pseudo file associated with that descriptor. It can be used to pass the output of a program as a seeming file to another program: diff <(sleep 1; date) <(sleep 2; date)

Check and modify format of variable in expect script

I am trying to verify that the format of a variable is a number and is at least 10 digits long with leading zeros, inside of an expect script.
In a bash script it would look something like this:
[[ "$var" != +([0-9]) ]] && echo "bad input" && exit
while [[ $(echo -n ${var} | wc -c) -lt 10 ]] ; do var="0${var}" ; done
For the following input:
16
I am trying to achieve the following output:
0000000016
The simplest way to check whether a variable has just digits is to use a regular expression. Expect's regular expressions are entirely up to the task:
if {![regexp {^\d+$} $var]} {
puts "bad input"
exit
}
Padding with zeroes is best done by formatting the value; if you know C's printf(), you'll recognize the format:
set var [format "%010d" $var]
Expect is actually just an extension of TCL, so you can use any facility that TCL provides. TCL is an unusual language, but it's not hard to do what you want.
# Set a test string.
set testvar 1234567890
# Store the match (if any) in matchvar.
regexp {\d{10,}} $testvar matchvar
puts $matchvar
# Test that matchvar holds an integer.
string is integer $matchvar
The string is command is relatively new, so you might have to rely on the return value of regexp if your TCL interpreter doesn't support it.

Compare integer in bash, unary operator expected

The following code gives
[: -ge: unary operator expected
when
i=0
if [ $i -ge 2 ]
then
#some code
fi
why?
Your problem arises from the fact that $i has a blank value when your statement fails. Always quote your variables when performing comparisons if there is the slightest chance that one of them may be empty, e.g.:
if [ "$i" -ge 2 ] ; then
...
fi
This is because of how the shell treats variables. Assume the original example,
if [ $i -ge 2 ] ; then ...
The first thing that the shell does when executing that particular line of code is substitute the value of $i, just like your favorite editor's search & replace function would. So assume that $i is empty or, even more illustrative, assume that $i is a bunch of spaces! The shell will replace $i as follows:
if [ -ge 2 ] ; then ...
Now that variable substitutions are done, the shell proceeds with the comparison and.... fails because it cannot see anything intelligible to the left of -gt. However, quoting $i:
if [ "$i" -ge 2 ] ; then ...
becomes:
if [ " " -ge 2 ] ; then ...
The shell now sees the double-quotes, and knows that you are actually comparing four blanks to 2 and will skip the if.
You also have the option of specifying a default value for $i if $i is blank, as follows:
if [ "${i:-0}" -ge 2 ] ; then ...
This will substitute the value 0 instead of $i is $i is undefined. I still maintain the quotes because, again, if $i is a bunch of blanks then it does not count as undefined, it will not be replaced with 0, and you will run into the problem once again.
Please read this when you have the time. The shell is treated like a black box by many, but it operates with very few and very simple rules - once you are aware of what those rules are (one of them being how variables work in the shell, as explained above) the shell will have no more secrets for you.
Judging from the error message the value of i was the empty string when you executed it, not 0.
I need to add my 5 cents. I see everybody use [ or [[, but it worth to mention that they are not part of if syntax.
For arithmetic comparisons, use ((...)) instead.
((...)) is an arithmetic command, which returns an exit status of 0 if
the expression is nonzero, or 1 if the expression is zero. Also used
as a synonym for "let", if side effects (assignments) are needed.
See: ArithmeticExpression
Your piece of script works just great. Are you sure you are not assigning anything else before the if to "i"?
A common mistake is also not to leave a space after and before the square brackets.

Resources