This question already has answers here:
How do I iterate over a range of numbers defined by variables in Bash?
(20 answers)
Closed last year.
when I populate an array using explicit range value it get populated correctly using the code below:
arr=()
for i in {0..10}
do
arr+=( 1 )
done
echo ${arr[#]}
and I got the following output (nothing surprising)
However, when storing the range in a variable and calling it as in the code below it only shows one element:
range=10
arr=()
for i in {0..$range}
do
arr+=( 1 )
done
echo ${arr[#]}
I got the following output
Variable expansion does not occur before brace expansion so you only get a literal {0..1} as a single iterated argument.
Use the old school method instead:
for (( i = 0; i <= range; ++i )); do
Used <= here because .. is inclusive.
Related
This question already has answers here:
How can I join elements of an array in Bash?
(34 answers)
Closed 4 months ago.
How do I convert an array into a string with a dash '-' in between in bash. For eg, I have this array
arr=(a b c d e)
I want to convert it into a string "a-b-c-d".
I figured out this "a-b-c-d-e," but there is an unwanted dash at the end. Is there an efficient way of doing this?
Thanks
This is where the "${arr[*]}" expansion form is useful (note the double quotes and the * index). This joins the array elements using the first character of the IFS variable
arr=(a b c d e)
joined=$(IFS='-'; echo "${arr[*]}")
declare -p joined
# => declare -- joined="a-b-c-d-e"
But if you want all-but-the-last elements, you'll combine this with the ${var:offset:length} expansion
joined=$(IFS='-'; echo "${arr[*]:0: ${#arr[#]} - 1}")
declare -p joined
# => declare -- joined="a-b-c-d"
This one's a little tricker. The offset and length parts of that expansion are arithmetic expressions. I'm calculating the length as "the number of elements in the array minus one".
Note how I'm defining IFS inside the command substitution parentheses: this is overriding the variable in the subshell of the command substitution, so it won't affect the IFS variable in your current shell.
Using awk if you want to remove the last entry
$ awk -vOFS=- '{NF--;$1=$1}1' <<<${arr[#]}
a-b-c-d
or the complete array
$ awk -vOFS=- '{$1=$1}1' <<<${arr[#]}
a-b-c-d-e
This question already has answers here:
Dynamic variable names in Bash
(19 answers)
Closed 4 years ago.
I have the variable $foo="something" and would like to use:
bar="foo"; echo $($bar)
to get "something" echoed.
In bash, you can use ${!variable} to use variable variables.
foo="something"
bar="foo"
echo "${!bar}"
# something
eval echo \"\$$bar\" would do it.
The accepted answer is great. However, #Edison asked how to do the same for arrays. The trick is that you want your variable holding the "[#]", so that the array is expanded with the "!". Check out this function to dump variables:
$ function dump_variables() {
for var in "$#"; do
echo "$var=${!var}"
done
}
$ STRING="Hello World"
$ ARRAY=("ab" "cd")
$ dump_variables STRING ARRAY ARRAY[#]
This outputs:
STRING=Hello World
ARRAY=ab
ARRAY[#]=ab cd
When given as just ARRAY, the first element is shown as that's what's expanded by the !. By giving the ARRAY[#] format, you get the array and all its values expanded.
To make it more clear how to do it with arrays:
arr=( 'a' 'b' 'c' )
# construct a var assigning the string representation
# of the variable (array) as its value:
var=arr[#]
echo "${!var}"
I'll try to explain:
I'm writing a bash script and I'm within a for loop. For all loops, I got a variable VAR_ID, that is used to store an identifier for another variable that will be exported when all work is done. For each single loop, I got a variable VAL. VAL's value should be assigned to VAR_ID's value. In the end, VAR_ID's value has to be an array in order to store all values.
Phew... well, it's a bit hard to explain for me, but I think this simple script should clarify what I'm trying to do (it's narrowed down from its actual purpose of course):
#!/bin/bash
COUNT=0
VAR_ID="my_array"
declare -a "$VAR_ID"
while (( $COUNT <= 2 ))
do
VAL=$RANDOM
$VAR_ID+=( "$VAL" ) # this doesn't work
(( COUNT++ ))
done
export $VAR_ID
This should result in a variable my_array and three random values in it. I tried using declare as in declare $VAR_ID=$VAL, but this doesn't work anymore if I use += instead of =.
COUNT can be used in a possible solution as position number or so if it helps as I have it also in my original script.
Thanks for any help in advance
Edit: I know there is a possibility with eval but I try to avoid using that until there is no other way.
I'm not sure what you're trying to do exactly, but you could use an associative array and export that. You can access elements of an associative array with variables in bash.
#!/usr/bin/env bash
declare -Ax arr # declare and export an associative array named 'arr'
declare -x var_id='array_position' # declare and export a variable named 'var_id'
for((i=0; i<=2; i++)); do
val=${RANDOM}
arr["${var_id}"]="${val}" # assign to the associative array with the var_id variable being the key, and val being the value
done
You can then access the associative array with variables, strings, or by iterating through it.
# Using an exact key name
echo "${arr[array_position]}"
# Using a variable which points to the key name
echo "${arr[${var_id}]}"
# Using a for loop
for key in "${!arr[#]}"; do
value="${arr[${key}]}"
echo "${key} ${value}"
done
From the bash manpage
When assigning to an associative array, the words in a compound
assignment may be either assignment statements, for which the
subscript is required, or a list of words that is interpreted as a
sequence of alternating keys and values: name=(key1 value1 key2 value2
… ). These are treated identically to name=( [key1]=value1
[key2]=value2 … ). The first word in the list determines how the
remaining words are interpreted; all assignments in a list must be of
the same type. When using key/value pairs, the keys may not be missing
or empty; a final missing value is treated like the empty string.
This syntax is also accepted by the declare builtin. Individual array
elements may be assigned to using the name[subscript]=value syntax
introduced above.
Here is an answer for my own question for anyone who encounters the same problem:
#!/bin/bash
COUNT=0
VAR_ID="my_array"
declare PSEUDOARRAY # is actually just a list of strings separated by spaces
while (( $COUNT <= 2 ))
do
VAL=$RANDOM
PSEUDOARRAY+="$VAL "
(( COUNT++ ))
done
declare -agx $VAR_ID='( $PSEUDOARRAY )' # "cast" PSEUDOARRAY on an actual array
This solves my problem. Note that if you don't do the "cast" part at the end an just use VAR_ID directly, the resulting my_array doesn't provide things like ${my_array[0]} to give only the first item or ${#my_array[#]} to count the items.
Also note: This method is limited as it does not distinguish between separator spaces and "value spaces" that may be stored in VAL.
This question already has answers here:
Variables in bash seq replacement ({1..10}) [duplicate]
(7 answers)
Brace expansion with variable? [duplicate]
(6 answers)
Closed 5 years ago.
I have script for getting some records:
#!/bin/bash
host_start=test
domain=test.com
for host in "${host_start}"{1..200}."$domain";
do
address=`dig +short $host`
echo "$address = $host"
done
In this case, everything is OK. I have:
192.168.1.1 = test1.test.com
192.168.1.2 = test2.test.com
192.168.1.3 = test3.test.com
...
...
...
etc ...
But instead of the literal {1..200}, I want to use variables in the start of my script. I did this:
t1=1
t2=200
for host in "${host_start}"{$t1..$t2}."$domain";
do
...
In this case, I get an error:
dig: 'test{1..200}.test.com' is not a legal name (empty label)
Where is my error? How do I fix it?
Brace expansion happens before variable expansion, so you can't use it with variables. Use a loop or the seq command.
for ((i=t1; i<=t2; i++)) ; do
host=$host_start$i.$domain
or
for i in $( seq $t1 $t2 ) ; do
host=$host_start$i.$domain
You should probably do:
for ((i=t1; i <= t2; i++)); do
host="${host_start}"$i."$domain"
...
done
This is probably a silly question, more out of curiosity. I have an array in bash:
array=(name1.ext name2.ext name3.ext)
I want to strip off the extension from each element. I was trying to do this by looping over each element, but I was having trouble setting the range of the loop (see below):
for i in 'echo {0..'expr ${#array[#]} - 1'}'; do
newarray[$i]+=$(echo "${array[$i]:0:5}");
done
Please note ' = "back-tick" within the code-block because I wasn't sure how to escape it.
I'm not able to just use a set range (e.g. seq 0 3), because it changes based on the folder, so I wanted to be able to use the length of the array minus 1. I was able to work around this using:
for (( i=0; i<${#array[#]}; i++ )); do
newarray[$i]+=$(echo "${array[$i]:0:5}"); done
But I thought there should be some way to do it with the "array length minus 1" method above and wondered how I was thinking about this incorrectly. Any pointers are appreciated.
Thanks!
Dan
You can apply various parameter expansion operators to each element of an array directly, without needing an explicit loop.
$ array=(name1.ext name2.ext name3.ext)
$ printf '%s\n' "${array[#]%.ext}"
name1
name2
name3
$ newarray=( "${array[#]%.ext}" )
In general, though, there is nearly never any need to generate a range numbers to iterate over. Just use the C-style for loop:
for ((i=0; i< ${#array[#]}; i++ )); do
newarray[$i]="${array[i]%.ext}"
done
With Bash, you could simply loop over your array elements with ${files[#]}:
#!/bin/bash
files=(name1.ext name2.ext name3.ext)
for f in ${files[#]}; do
echo "${f%.*}"
done
Also substring removal ${f%.*} is a better choice if you have extensions of different lengths.
You can use the seq command
for i in `seq 0 $(( ${#array[#]} - 1 ))`
do
···
done
or the bash brace expansion (but in this case you need eval):
for i in `eval echo {0..$(( ${#array[#]} - 1 ))}`
do
···
done
But there is another better (it works even in sparse arrays): let's make bash give us the array indexes:
for i in ${!array[#]}
do
···
done