How to construct variable name in Bash? - bash

I want to construct variable name N_foo and N_bar and use their values in the following:
#!/bin/bash
N_foo=2
N_bar=3
for i in { "foo" "bar" }
do
for j in { 1..$(`N_$i`) }
do
echo $j
done
done
I want to use the values of N_foo and N_bar in the two inner loops and print out 1, 2 and 1, 2, 3, respectively. What's the correct syntax?

#!/bin/bash
N_foo=2
N_bar=3
for i in "foo" "bar"
do
key="N_${i}"
eval count='$'$key
for j in `seq 1 $count`
do
echo $j
done
done

You can use the indirect variable reference operator:
Example
var="foo"
nfoo=1
ref=n${var}
echo $ref
echo ${!ref}
Which gives this output:
nfoo
1

#!/bin/bash
N_foo=2
N_bar=3
for i in "foo" "bar"
do
i2="N_$i"
seq 1 ${!i2}
done

I ended up using the following code. It uses the parameter substitution technique (c.f. http://tldp.org/LDP/abs/html/parameter-substitution.html).
#!/bin/bash
N_foo=2
N_bar=3
for i in "foo" "bar"
do
j_max=N_$i
for (( j=1; j<=${!j_max}; j++ ))
do
echo $j
done
done
The ! is history expansion parameter (c.f. http://www.gnu.org/software/bash/manual/bashref.html#Special-Parameters). !j_max will be replaced by the most recent value set to j_max which is N_foo/N_bar in 1st/2nd iteration. It then calls ${N_foo}/${N_bar} which has value 2/3 in the 1st/2nd iteration.

Related

The meaning of ${TF_CFLAGS[#]} [duplicate]

How do I create an array in unix shell scripting?
The following code creates and prints an array of strings in shell:
#!/bin/bash
array=("A" "B" "ElementC" "ElementE")
for element in "${array[#]}"
do
echo "$element"
done
echo
echo "Number of elements: ${#array[#]}"
echo
echo "${array[#]}"
Result:
A
B
ElementC
ElementE
Number of elements: 4
A B ElementC ElementE
in bash, you create array like this
arr=(one two three)
to call the elements
$ echo "${arr[0]}"
one
$ echo "${arr[2]}"
three
to ask for user input, you can use read
read -p "Enter your choice: " choice
Bourne shell doesn't support arrays. However, there are two ways to handle the issue.
Use positional shell parameters $1, $2, etc.:
$ set one two three
$ echo $*
one two three
$ echo $#
3
$ echo $2
two
Use variable evaluations:
$ n=1 ; eval a$n="one"
$ n=2 ; eval a$n="two"
$ n=3 ; eval a$n="three"
$ n=2
$ eval echo \$a$n
two
#!/bin/bash
# define a array, space to separate every item
foo=(foo1 foo2)
# access
echo "${foo[1]}"
# add or changes
foo[0]=bar
foo[2]=cat
foo[1000]=also_OK
You can read the ABS "Advanced Bash-Scripting Guide"
The Bourne shell and C shell don't have arrays, IIRC.
In addition to what others have said, in Bash you can get the number of elements in an array as follows:
elements=${#arrayname[#]}
and do slice-style operations:
arrayname=(apple banana cherry)
echo ${arrayname[#]:1} # yields "banana cherry"
echo ${arrayname[#]: -1} # yields "cherry"
echo ${arrayname[${#arrayname[#]}-1]} # yields "cherry"
echo ${arrayname[#]:0:2} # yields "apple banana"
echo ${arrayname[#]:1:1} # yields "banana"
Try this :
echo "Find the Largest Number and Smallest Number of a given number"
echo "---------------------------------------------------------------------------------"
echo "Enter the number"
read n
i=0
while [ $n -gt 0 ] #For Seperating digits and Stored into array "x"
do
x[$i]=`expr $n % 10`
n=`expr $n / 10`
i=`expr $i + 1`
done
echo "Array values ${x[#]}" # For displaying array elements
len=${#x[*]} # it returns the array length
for (( i=0; i<len; i++ )) # For Sorting array elements using Bubble sort
do
for (( j=i+1; j<len; j++ ))
do
if [ `echo "${x[$i]} > ${x[$j]}"|bc` ]
then
t=${x[$i]}
t=${x[$i]}
x[$i]=${x[$j]}
x[$j]=$t
fi
done
done
echo "Array values ${x[*]}" # Displaying of Sorted Array
for (( i=len-1; i>=0; i-- )) # Form largest number
do
a=`echo $a \* 10 + ${x[$i]}|bc`
done
echo "Largest Number is : $a"
l=$a #Largest number
s=0
while [ $a -gt 0 ] # Reversing of number, We get Smallest number
do
r=`expr $a % 10`
s=`echo "$s * 10 + $r"|bc`
a=`expr $a / 10`
done
echo "Smallest Number is : $s" #Smallest Number
echo "Difference between Largest number and Smallest number"
echo "=========================================="
Diff=`expr $l - $s`
echo "Result is : $Diff"
echo "If you try it, We can get it"
Your question asks about "unix shell scripting", but is tagged bash. Those are two different answers.
The POSIX specification for shells does not have anything to say about arrays, as the original Bourne shell did not support them. Even today, on FreeBSD, Ubuntu Linux, and many other systems, /bin/sh does not have array support. So if you want your script to work in different Bourne-compatible shells, you shouldn't use them. Alternatively, if you are assuming a specific shell, then be sure to put its full name in the shebang line, e.g. #!/usr/bin/env bash.
If you are using bash or zsh, or a modern version of ksh, you can create an array like this:
myArray=(first "second element" 3rd)
and access elements like this
$ echo "${myArray[1]}" # for bash/ksh; for zsh, echo $myArray[2]
second element
You can get all the elements via "${myArray[#]}". You can use the slice notation ${array[#]:start:length} to restrict the portion of the array referenced, e.g. "${myArray[#]:1}" to leave off the first element.
The length of the array is ${#myArray[#]}. You can get a new array containing all the indexes from an existing array with "${!myArray[#]}".
Older versions of ksh before ksh93 also had arrays, but not the parenthesis-based notation, nor did they support slicing. You could create an array like this, though:
set -A myArray -- first "second element" 3rd
You can try of the following type :
#!/bin/bash
declare -a arr
i=0
j=0
for dir in $(find /home/rmajeti/programs -type d)
do
arr[i]=$dir
i=$((i+1))
done
while [ $j -lt $i ]
do
echo ${arr[$j]}
j=$((j+1))
done
An array can be loaded in twoways.
set -A TEST_ARRAY alpha beta gamma
or
X=0 # Initialize counter to zero.
-- Load the array with the strings alpha, beta, and gamma
for ELEMENT in alpha gamma beta
do
TEST_ARRAY[$X]=$ELEMENT
((X = X + 1))
done
Also, I think below information may help:
The shell supports one-dimensional arrays. The maximum number of array
elements is 1,024. When an array is defined, it is automatically
dimensioned to 1,024 elements. A one-dimensional array contains a
sequence of array elements, which are like the boxcars connected
together on a train track.
In case you want to access the array:
echo ${MY_ARRAY[2] # Show the third array element
gamma
echo ${MY_ARRAY[*] # Show all array elements
- alpha beta gamma
echo ${MY_ARRAY[#] # Show all array elements
- alpha beta gamma
echo ${#MY_ARRAY[*]} # Show the total number of array elements
- 3
echo ${#MY_ARRAY[#]} # Show the total number of array elements
- 3
echo ${MY_ARRAY} # Show array element 0 (the first element)
- alpha
If you want a key value store with support for spaces use the -A parameter:
declare -A programCollection
programCollection["xwininfo"]="to aquire information about the target window."
for program in ${!programCollection[#]}
do
echo "The program ${program} is used ${programCollection[${program}]}"
done
http://linux.die.net/man/1/bash "Associative arrays are created using declare -A name. "
There are multiple ways to create an array in shell.
ARR[0]="ABC"
ARR[1]="BCD"
echo ${ARR[*]}
${ARR[*]} prints all elements in the array.
Second way is:
ARR=("A" "B" "C" "D" 5 7 "J")
echo ${#ARR[#]}
echo ${ARR[0]}
${#ARR[#]} is used to count length of the array.
To read the values from keybord and insert element into array
# enter 0 when exit the insert element
echo "Enter the numbers"
read n
while [ $n -ne 0 ]
do
x[$i]=`expr $n`
read n
let i++
done
#display the all array elements
echo "Array values ${x[#]}"
echo "Array values ${x[*]}"
# To find the array length
length=${#x[*]}
echo $length
A Simple way :
arr=("sharlock" "bomkesh" "feluda" ) ##declare array
len=${#arr[*]} #determine length of array
# iterate with for loop
for (( i=0; i<len; i++ ))
do
echo ${arr[$i]}
done
In ksh you do it:
set -A array element1 element2 elementn
# view the first element
echo ${array[0]}
# Amount elements (You have to substitute 1)
echo ${#array[*]}
# show last element
echo ${array[ $(( ${#array[*]} - 1 )) ]}

bash substitution call that increments a variable

I'm trying to define a bash function returning an incremented id
that I can access directly using bash substitution:
#!/bin/bash
getId() {
echo "$x"
x=$((x+1))
}
x=0
echo "id1: $(getId)"
echo "id2: $(getId)"
However the variable is not incremented and I cannot figure out why.
id1: 0
id2: 0
Please, does someone have an explanation for this behaviour?
getId() {
echo "$x"
((x++))
}
x=0
echo -n "id1: "
getId
echo -n "id2: "
getId
Output:
id1: 0
id2: 1
There is no easy way I know of to do it in a sub-shell call using the syntax you have (in the echo line).
An alternate would be:
#!/bin/bash
export x=0
incId() {
#echo "$x"
(( x += 1))
}
incId
echo "id1: $x"
incId
echo "id2: $x"
But here you need the out-of-the-echo-line incId function call to get the id incremented.
It also starts counting from 1, not 0.
Using the let shell command is the better way to do math too.
Using (( ... )) is the right way to do shell arithmetic
Might as well make it generic:
incr() { (( $1 += ${2:-1} )); }
Examples:
incr x ; echo $x # => 1
incr x ; echo $x # => 2
incr x 4; echo $x # => 6
incr x -2; echo $x # => 4

How to fetch last argument and stop before last arguments in shell script?

I want to merge all files into one. Here, the last argument is the destination file name.
I want to take last argument and then in loop stop before last arguments.
Here code is given that I want to implement:
echo "No. of Argument : $#"
for i in $* - 1
do
echo $i
cat $i >> last argument(file)
done
How to achieve that?
Using bash:
fname=${!#}
for a in "${#:1:$# - 1}"
do
echo "$a"
cat "$a" >>"$fname"
done
In bash, the last argument to a script is ${!#}. So, that is where we get the file name.
bash also allows selecting elements from an array. To start with a simple example, observe:
$ set -- a b c d e f
$ echo "${#}"
a b c d e f
$ echo "${#:2:4}"
b c d e
In our case, we want to select elements from the first to the second to last. The first is number 1. The last is number $#. We want to select all but the last. WE thus want $# - 1 elements of the array. Therefore, to select the arguments from the first to the second to last, we use:
${#:1:$# - 1}
A POSIX-compliant method:
eval last_arg=\$$#
while [ $# -ne 1 ]; do
echo "$1"
cat "$1" >> "$last_arg"
shift
done
Here, eval is safe, because you are only expanding a read-only parameter in the string that eval will execute. If you don't want to unset the positional parameters via shift, you can iterate over them, using a counter to break out of the loop early.
eval last_arg=\$$#
i=1
for arg in "$#"; do
echo "$arg"
cat "$arg" >> "$last_arg"
i=$((i+1))
if [ "$i" = "$#" ]; then
break
fi
done

Iterate over array

In a shell script I'm looking to iterate over an array like I would in python by doing:
for i, j in (("i value", "j value"), ("Another I value", "another j value")):
# Do stuff with i and j
print i, j
But can't work out the best way to do it? I'm tempted to rewrite the shell script in python but that seems awfully heavy for what I'm trying to do.
In this instance I would do:
while [ $# -ge 2 ]; do
PATH="$1"; shift
REPO="$1"; shift
# ... Do stuff with $PATH and $REPO here
done
Note that each time you reference variables ($1, $PATH, and especially $#, you want to surround them with "" quotes - that way you avoid issues when there are spaces in the values.
There are any number of ways to do this. Here's one using a here doc:
foo () {
while IFS=$1 read i j
do
echo "i is $i"
echo "j is $j"
done
}
foo '|' <<EOF
i value|j value
Another I value|another j value
EOF
Posting here the current kludge I'm using to do it..
#!/bin/bash
function pull_or_clone {
PATH=$1
shift
REPO=$1
shift
echo Path is $PATH
echo Repo is $REPO
# Do stuff with $PATH and $REPO here..
#Nasty bashism right here.. Can't seem to make it work with spaces int he string
RAWP=$#
RAWP=${#RAWP}
if [ $RAWP -gt 0 ]; then
pull_or_clone $#
fi
}
pull_or_clone path repo pairs go here

bash: local: `0': not a valid identifier

When I call for example encrypt HI the result is:
bash: local: `0': not a valid identifier
bash: local: `1': not a valid identifier
Here is the code:
#!/bin/bash
encrypt(){
local s="$1";
local lenght=${#s};
local i=0
while [ $i -lt $lenght ]
do
local j=1
local letter=expr substr $s $i $j;
letterToNumber $letter;
echo $number;
i=$[$i+$j];
done
}
> local letter=expr substr $s $i $j;
Looks like this is your problem. This is a newbie mistake where apparently you meant to write
local letter=$(expr substr "$s" "$i" "$j")
Newcomers sometimes seem to have a hard time with shell syntax. The shell simply tokenizes your command from the left; the first token which does not contain = is a keyword or a command, and (in the general case) the rest of the tokens are just text to pass as arguments to that command. So letter=expr substr will assign the value expr to the variable letter and (try to) run the command substr; and local foo=bar baz quux will declare the variables foo, baz, and quux as local, and assign the value bar to foo while we are at it.
Using expr is very rarely what you want to do in a modern shell script. Bash has a built-in substring operator; ${s:$i-1:$j}
encrypt () {
for((i=0; i<${#1}; ++i)); do
letterToNumber "${1:i:1}"
done
}
(I am guessing you also meant to say number=$(letterToNumber "${1:i:1}"); echo "$number" but that's just a useless use of echo.)
tripleee is right.
And though I do not have your letterToNumber func/program, I think there's 2 other problems:
#!/bin/bash
#set -v # always handy
#set -e # handy too
encrypt(){
local s="$1";
local lenght=${#s};
local i=1 # <-------- start at 1, not 0
while [ $i -le $lenght ] # <-------- le, not lt
do
local j=1
local letter=$(expr substr $s $i $j);
echo letter $letter
#letterToNumber $letter;
echo $number;
i=$[$i+$j];
done
}
encrypt HELLO
Output:
$ ./foobar.sh
letter H
letter E
letter L
letter L
letter O
Instead of local letter=expr substr $s $i $j;, which is incorrect (to know why, see answer by tripleee), you could do:
# Insert this line before `local s="$1";` or anywhere else before while loop
local letter
# Inside the "while loop"...
letter=${s:$i:$j}
To calculate i=i+j, its better to use shorter (than yourth) BASH internal math syntax:
# Replace `local i=0` with this line:
local -i i=0 j=1
# i=i+j . Note: you dont need "$" here!
i+=j
Finally overall encrypt function code will be:
encrypt () {
local s="$1" letter length=${#s}
local -i i=0 j=1
for ((; i<length; i+=j )); do
letter=${s:$i:$j}
letterToNumber "$letter"
echo $number
done
return 0
}
This code may be replaced by the simplest one-liner, but... you dont answer (yet) about how to do that :)

Resources