Sorry for a bad title, please feel free to adjust it to something more appropriate.
How can I index arrays using zsh or bash scripting as I am doing for lists in R below;
# Some lists with the same number of elements
list1 <- list(sample(letters,10))
list2 <- list(1:10)
for(i in 1:length(list1)) {
a <- list1[[1]][i]
b <- list2[[1]][i]
}
print(paste(a,b)) # Or some other function where a and b are used simultaneously
[1] "f 1"
[1] "e 2"
[1] "t 3"
[1] "s 4"
[1] "c 5"
[1] "o 6"
[1] "p 7"
[1] "y 8"
[1] "k 9"
[1] "d 10"
The below code obviously only prints the last elements from both lists, since I have not found a way to do 1 to the length of array
# dummy data
echo 'A 1' > A.txt
echo 'B 1' > B.txt
echo 'C 1' > C.txt
echo 'A,2' > A.csv
echo 'B,2' > B.csv
echo 'C,2' > C.csv
txtlist=(*.txt) # create an array of .txt files
csvlist=(*.csv) # create an array of .csv files
# in zsh $#array returns the length of the array, so
for i in $#txtlist; do
a=$txtlist[i]
b=$csvlist[i]
echo $a,$b # Or some other function where a and b are used simultaneously
done
#C.txt,C.csv
Any pointers would be very much appreciated, thanks!
bash and zsh both know C-style for-loops:
From man 1 zshmisc (man 1 bash is essentially the same):
for (( [expr1] ; [expr2] ; [expr3] )) do list done
The arithmetic expression expr1 is evaluated first (see the section `Arithmetic Evaluation'). The arithmetic expression expr2 is repeatedly
evaluated until it evaluates to zero and when non-zero, list is executed and the arithmetic expression expr3 evaluated. If any expression is
omitted, then it behaves as if it evaluated to 1.
Example for zsh:
for (( i=1; i<=$#txtlist; i++ )); do
echo "$txtlist[$i]" "$csvlist[$i]"
done
Example for bash:
for (( i=0; i<=${#txtlist[#]}; i++ )); do
echo "${txtlist[$i]}" "${csvlist[$i]}"
done
I am not sure to understand your example, I'm sorry, but, you can do loop like that in bash :
myLength=${#myArray[#]}
for (( i=1; i<${myLength}; i++ ));
do
echo ${myArray[$i]}
done
Use the following syntax:
$ x=(1 2 3 4 5)
$ print $x
1 2 3 4 5
$ print ${x:1}
2 3 4 5
Related
This question already has answers here:
How to iterate over an array using indirect reference?
(7 answers)
Closed 3 years ago.
I'd like to use arrays in BASH properly when using indirection ${!varname}.
Here's my sample script:
#!/bin/bash
i="1 2 3"
x=CONFIG
y1=( "A and B" "B and C" )
# y1=( "\"A and B\"" "\"B and C\"" )
y2=( "ABC and D" )
y3=
echo "y1=${y1[#]}"
echo "y2=${y2[#]}"
echo "y3=${y3[#]}"
echo "==="
for z in $i
do
t=y${z}
tval=( ${!t} )
r=0
echo "There are ${#tval[#]} elements in ${t}."
if [ ${#tval[#]} -gt 0 ]; then
r=1
echo "config_y${z}=\""
fi
for tv in "${tval[#]}"
do
[ -n "${tv}" ] && echo "${tv}"
done
if [ "x$r" == "x1" ]; then
echo "\""
fi
done
Here's the result:
y1=A and B B and C
y2=ABC and D
y3=
===
There are 3 elements in y1.
config_y1="
A
and
B
"
There are 3 elements in y2.
config_y2="
ABC
and
D
"
There are 0 elements in y3.
What I would like to get instead is:
y1=A and B B and C
y2=ABC and D
y3=
===
There are 2 elements in y1.
config_y1="
A and B
B and C
"
There are 1 elements in y2.
config_y2="
ABC and D
"
There are 0 elements in y3.
I also tried to run something like this:
#!/bin/bash
i="1 2 3"
x=CONFIG
y1=( "A and B" "B and C" )
# y1=( "\"A and B\"" "\"B and C\"" )
y2=( "ABC and D" )
y3=
for variable in ${!y#}
do
echo "$variable" # This is the content of $variable
echo "${variable[#]}" # So is this
echo "${!variable}" # This shows first element of the indexed array
echo "${!variable[#]}" # Not what I wanted
echo "${!variable[0]} ${!variable[1]}" # Not what I wanted
echo "---"
done
Ideally, ${!Variable[#]} should do what I want, but it doesn't.
Also, ${!Variable} only shows the first element of the array,
What can I try?
Your syntax for accessing arrays is wrong here:
tval=( ${!t} )
This will evaluate to e.g. $y1 but you want "${y1[#]}" which is how you address an array with that name with proper quoting.
Unfortunately, there is no straightforward way to refer to an array through indirection, but see Indirect reference to array values in bash for a couple of workarounds.
Notice also how
y3=()
is different from
y3=
Using variables for stuff which isn't actually variable is a bit of a code smell, too.
#!/bin/bash
i="1 2 3"
x=CONFIG
y1=( "A and B" "B and C" )
y2=( "ABC and D" )
# Fix y3 assignment
y3=()
echo "y1=${y1[#]}"
echo "y2=${y2[#]}"
echo "y3=${y3[#]}"
echo "==="
for z in $i
do
# Add [#] as in Aaron's answer to the linked question
t=y${z}[#]
# And (always!) quote the variable interpolation
tval=( "${!t}" )
r=0
echo "There are ${#tval[#]} elements in ${t}."
if [ ${#tval[#]} -gt 0 ]; then
r=1
echo "config_y${z}=\""
fi
for tv in "${tval[#]}"
do
[ -n "${tv}" ] && echo "${tv}"
done
if [ "x$r" = "x1" ]; then
echo "\""
fi
done
Probably also investigate printf for unambiguously printing values.
I need to remove an element from an array in bash shell.
Generally I'd simply do:
array=("${(#)array:#<element to remove>}")
Unfortunately the element I want to remove is a variable so I can't use the previous command.
Down here an example:
array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[#]/$delete} ) -> but clearly doesn't work because of {}
Any idea?
The following works as you would like in bash and zsh:
$ array=(pluto pippo)
$ delete=pluto
$ echo ${array[#]/$delete}
pippo
$ array=( "${array[#]/$delete}" ) #Quotes when working with strings
If need to delete more than one element:
...
$ delete=(pluto pippo)
for del in ${delete[#]}
do
array=("${array[#]/$del}") #Quotes when working with strings
done
Caveat
This technique actually removes prefixes matching $delete from the elements, not necessarily whole elements.
Update
To really remove an exact item, you need to walk through the array, comparing the target to each element, and using unset to delete an exact match.
array=(pluto pippo bob)
delete=(pippo)
for target in "${delete[#]}"; do
for i in "${!array[#]}"; do
if [[ ${array[i]} = $target ]]; then
unset 'array[i]'
fi
done
done
Note that if you do this, and one or more elements is removed, the indices will no longer be a continuous sequence of integers.
$ declare -p array
declare -a array=([0]="pluto" [2]="bob")
The simple fact is, arrays were not designed for use as mutable data structures. They are primarily used for storing lists of items in a single variable without needing to waste a character as a delimiter (e.g., to store a list of strings which can contain whitespace).
If gaps are a problem, then you need to rebuild the array to fill the gaps:
for i in "${!array[#]}"; do
new_array+=( "${array[i]}" )
done
array=("${new_array[#]}")
unset new_array
You could build up a new array without the undesired element, then assign it back to the old array. This works in bash:
array=(pluto pippo)
new_array=()
for value in "${array[#]}"
do
[[ $value != pluto ]] && new_array+=($value)
done
array=("${new_array[#]}")
unset new_array
This yields:
echo "${array[#]}"
pippo
This is the most direct way to unset a value if you know it's position.
$ array=(one two three)
$ echo ${#array[#]}
3
$ unset 'array[1]'
$ echo ${array[#]}
one three
$ echo ${#array[#]}
2
This answer is specific to the case of deleting multiple values from large arrays, where performance is important.
The most voted solutions are (1) pattern substitution on an array, or (2) iterating over the array elements. The first is fast, but can only deal with elements that have distinct prefix, the second has O(n*k), n=array size, k=elements to remove. Associative array are relative new feature, and might not have been common when the question was originally posted.
For the exact match case, with large n and k, possible to improve performance from O(nk) to O(n+klog(k)). In practice, O(n) assuming k much lower than n. Most of the speed up is based on using associative array to identify items to be removed.
Performance (n-array size, k-values to delete). Performance measure seconds of user time
N K New(seconds) Current(seconds) Speedup
1000 10 0.005 0.033 6X
10000 10 0.070 0.348 5X
10000 20 0.070 0.656 9X
10000 1 0.043 0.050 -7%
As expected, the current solution is linear to N*K, and the fast solution is practically linear to K, with much lower constant. The fast solution is slightly slower vs the current solution when k=1, due to additional setup.
The 'Fast' solution: array=list of input, delete=list of values to remove.
declare -A delk
for del in "${delete[#]}" ; do delk[$del]=1 ; done
# Tag items to remove, based on
for k in "${!array[#]}" ; do
[ "${delk[${array[$k]}]-}" ] && unset 'array[k]'
done
# Compaction
array=("${array[#]}")
Benchmarked against current solution, from the most-voted answer.
for target in "${delete[#]}"; do
for i in "${!array[#]}"; do
if [[ ${array[i]} = $target ]]; then
unset 'array[i]'
fi
done
done
array=("${array[#]}")
Here's a one-line solution with mapfile:
$ mapfile -d $'\0' -t arr < <(printf '%s\0' "${arr[#]}" | grep -Pzv "<regexp>")
Example:
$ arr=("Adam" "Bob" "Claire"$'\n'"Smith" "David" "Eve" "Fred")
$ echo "Size: ${#arr[*]} Contents: ${arr[*]}"
Size: 6 Contents: Adam Bob Claire
Smith David Eve Fred
$ mapfile -d $'\0' -t arr < <(printf '%s\0' "${arr[#]}" | grep -Pzv "^Claire\nSmith$")
$ echo "Size: ${#arr[*]} Contents: ${arr[*]}"
Size: 5 Contents: Adam Bob David Eve Fred
This method allows for great flexibility by modifying/exchanging the grep command and doesn't leave any empty strings in the array.
Partial answer only
To delete the first item in the array
unset 'array[0]'
To delete the last item in the array
unset 'array[-1]'
To expand on the above answers, the following can be used to remove multiple elements from an array, without partial matching:
ARRAY=(one two onetwo three four threefour "one six")
TO_REMOVE=(one four)
TEMP_ARRAY=()
for pkg in "${ARRAY[#]}"; do
for remove in "${TO_REMOVE[#]}"; do
KEEP=true
if [[ ${pkg} == ${remove} ]]; then
KEEP=false
break
fi
done
if ${KEEP}; then
TEMP_ARRAY+=(${pkg})
fi
done
ARRAY=("${TEMP_ARRAY[#]}")
unset TEMP_ARRAY
This will result in an array containing:
(two onetwo three threefour "one six")
Here's a (probably very bash-specific) little function involving bash variable indirection and unset; it's a general solution that does not involve text substitution or discarding empty elements and has no problems with quoting/whitespace etc.
delete_ary_elmt() {
local word=$1 # the element to search for & delete
local aryref="$2[#]" # a necessary step since '${!$2[#]}' is a syntax error
local arycopy=("${!aryref}") # create a copy of the input array
local status=1
for (( i = ${#arycopy[#]} - 1; i >= 0; i-- )); do # iterate over indices backwards
elmt=${arycopy[$i]}
[[ $elmt == $word ]] && unset "$2[$i]" && status=0 # unset matching elmts in orig. ary
done
return $status # return 0 if something was deleted; 1 if not
}
array=(a 0 0 b 0 0 0 c 0 d e 0 0 0)
delete_ary_elmt 0 array
for e in "${array[#]}"; do
echo "$e"
done
# prints "a" "b" "c" "d" in lines
Use it like delete_ary_elmt ELEMENT ARRAYNAME without any $ sigil. Switch the == $word for == $word* for prefix matches; use ${elmt,,} == ${word,,} for case-insensitive matches; etc., whatever bash [[ supports.
It works by determining the indices of the input array and iterating over them backwards (so deleting elements doesn't screw up iteration order). To get the indices you need to access the input array by name, which can be done via bash variable indirection x=1; varname=x; echo ${!varname} # prints "1".
You can't access arrays by name like aryname=a; echo "${$aryname[#]}, this gives you an error. You can't do aryname=a; echo "${!aryname[#]}", this gives you the indices of the variable aryname (although it is not an array). What DOES work is aryref="a[#]"; echo "${!aryref}", which will print the elements of the array a, preserving shell-word quoting and whitespace exactly like echo "${a[#]}". But this only works for printing the elements of an array, not for printing its length or indices (aryref="!a[#]" or aryref="#a[#]" or "${!!aryref}" or "${#!aryref}", they all fail).
So I copy the original array by its name via bash indirection and get the indices from the copy. To iterate over the indices in reverse I use a C-style for loop. I could also do it by accessing the indices via ${!arycopy[#]} and reversing them with tac, which is a cat that turns around the input line order.
A function solution without variable indirection would probably have to involve eval, which may or may not be safe to use in that situation (I can't tell).
Using unset
To remove an element at particular index, we can use unset and then do copy to another array. Only just unset is not required in this case. Because unset does not remove the element it just sets null string to the particular index in array.
declare -a arr=('aa' 'bb' 'cc' 'dd' 'ee')
unset 'arr[1]'
declare -a arr2=()
i=0
for element in "${arr[#]}"
do
arr2[$i]=$element
((++i))
done
echo "${arr[#]}"
echo "1st val is ${arr[1]}, 2nd val is ${arr[2]}"
echo "${arr2[#]}"
echo "1st val is ${arr2[1]}, 2nd val is ${arr2[2]}"
Output is
aa cc dd ee
1st val is , 2nd val is cc
aa cc dd ee
1st val is cc, 2nd val is dd
Using :<idx>
We can remove some set of elements using :<idx> also. For example if we want to remove 1st element we can use :1 as mentioned below.
declare -a arr=('aa' 'bb' 'cc' 'dd' 'ee')
arr2=("${arr[#]:1}")
echo "${arr2[#]}"
echo "1st val is ${arr2[1]}, 2nd val is ${arr2[2]}"
Output is
bb cc dd ee
1st val is cc, 2nd val is dd
http://wiki.bash-hackers.org/syntax/pe#substring_removal
${PARAMETER#PATTERN} # remove from beginning
${PARAMETER##PATTERN} # remove from the beginning, greedy match
${PARAMETER%PATTERN} # remove from the end
${PARAMETER%%PATTERN} # remove from the end, greedy match
In order to do a full remove element, you have to do an unset command with an if statement. If you don't care about removing prefixes from other variables or about supporting whitespace in the array, then you can just drop the quotes and forget about for loops.
See example below for a few different ways to clean up an array.
options=("foo" "bar" "foo" "foobar" "foo bar" "bars" "bar")
# remove bar from the start of each element
options=("${options[#]/#"bar"}")
# options=("foo" "" "foo" "foobar" "foo bar" "s" "")
# remove the complete string "foo" in a for loop
count=${#options[#]}
for ((i = 0; i < count; i++)); do
if [ "${options[i]}" = "foo" ] ; then
unset 'options[i]'
fi
done
# options=( "" "foobar" "foo bar" "s" "")
# remove empty options
# note the count variable can't be recalculated easily on a sparse array
for ((i = 0; i < count; i++)); do
# echo "Element $i: '${options[i]}'"
if [ -z "${options[i]}" ] ; then
unset 'options[i]'
fi
done
# options=("foobar" "foo bar" "s")
# list them with select
echo "Choose an option:"
PS3='Option? '
select i in "${options[#]}" Quit
do
case $i in
Quit) break ;;
*) echo "You selected \"$i\"" ;;
esac
done
Output
Choose an option:
1) foobar
2) foo bar
3) s
4) Quit
Option?
Hope that helps.
There is also this syntax, e.g. if you want to delete the 2nd element :
array=("${array[#]:0:1}" "${array[#]:2}")
which is in fact the concatenation of 2 tabs. The first from the index 0 to the index 1 (exclusive) and the 2nd from the index 2 to the end.
POSIX shell script does not have arrays.
So most probably you are using a specific dialect such as bash, korn shells or zsh.
Therefore, your question as of now cannot be answered.
Maybe this works for you:
unset array[$delete]
What I do is:
array="$(echo $array | tr ' ' '\n' | sed "/itemtodelete/d")"
BAM, that item is removed.
This is a quick-and-dirty solution that will work in simple cases but will break if (a) there are regex special characters in $delete, or (b) there are any spaces at all in any items. Starting with:
array+=(pluto)
array+=(pippo)
delete=(pluto)
Delete all entries exactly matching $delete:
array=(`echo $array | fmt -1 | grep -v "^${delete}$" | fmt -999999`)
resulting in
echo $array -> pippo, and making sure it's an array:
echo $array[1] -> pippo
fmt is a little obscure: fmt -1 wraps at the first column (to put each item on its own line. That's where the problem arises with items in spaces.) fmt -999999 unwraps it back to one line, putting back the spaces between items. There are other ways to do that, such as xargs.
Addendum: If you want to delete just the first match, use sed, as described here:
array=(`echo $array | fmt -1 | sed "0,/^${delete}$/{//d;}" | fmt -999999`)
Actually, I just noticed that the shell syntax somewhat has a behavior built-in that allows for easy reconstruction of the array when, as posed in the question, an item should be removed.
# let's set up an array of items to consume:
x=()
for (( i=0; i<10; i++ )); do
x+=("$i")
done
# here, we consume that array:
while (( ${#x[#]} )); do
i=$(( $RANDOM % ${#x[#]} ))
echo "${x[i]} / ${x[#]}"
x=("${x[#]:0:i}" "${x[#]:i+1}")
done
Notice how we constructed the array using bash's x+=() syntax?
You could actually add more than one item with that, the content of a whole other array at once.
In ZSH this is dead easy (note this uses more bash compatible syntax than necessary where possible for ease of understanding):
# I always include an edge case to make sure each element
# is not being word split.
start=(one two three 'four 4' five)
work=(${(#)start})
idx=2
val=${work[idx]}
# How to remove a single element easily.
# Also works for associative arrays (at least in zsh)
work[$idx]=()
echo "Array size went down by one: "
[[ $#work -eq $(($#start - 1)) ]] && echo "OK"
echo "Array item "$val" is now gone: "
[[ -z ${work[(r)$val]} ]] && echo OK
echo "Array contents are as expected: "
wanted=("${start[#]:0:1}" "${start[#]:2}")
[[ "${(j.:.)wanted[#]}" == "${(j.:.)work[#]}" ]] && echo "OK"
echo "-- array contents: start --"
print -l -r -- "-- $#start elements" ${(#)start}
echo "-- array contents: work --"
print -l -r -- "-- $#work elements" "${work[#]}"
Results:
Array size went down by one:
OK
Array item two is now gone:
OK
Array contents are as expected:
OK
-- array contents: start --
-- 5 elements
one
two
three
four 4
five
-- array contents: work --
-- 4 elements
one
three
four 4
five
To avoid conflicts with array index using unset - see https://stackoverflow.com/a/49626928/3223785 and https://stackoverflow.com/a/47798640/3223785 for more information - reassign the array to itself: ARRAY_VAR=(${ARRAY_VAR[#]}).
#!/bin/bash
ARRAY_VAR=(0 1 2 3 4 5 6 7 8 9)
unset ARRAY_VAR[5]
unset ARRAY_VAR[4]
ARRAY_VAR=(${ARRAY_VAR[#]})
echo ${ARRAY_VAR[#]}
A_LENGTH=${#ARRAY_VAR[*]}
for (( i=0; i<=$(( $A_LENGTH -1 )); i++ )) ; do
echo ""
echo "INDEX - $i"
echo "VALUE - ${ARRAY_VAR[$i]}"
done
exit 0
[Ref.: https://tecadmin.net/working-with-array-bash-script/ ]
How about something like:
array=(one two three)
array_t=" ${array[#]} "
delete=one
array=(${array_t// $delete / })
unset array_t
#/bin/bash
echo "# define array with six elements"
arr=(zero one two three 'four 4' five)
echo "# unset by index: 0"
unset -v 'arr[0]'
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done
arr_delete_by_content() { # value to delete
for i in ${!arr[*]}; do
[ "${arr[$i]}" = "$1" ] && unset -v 'arr[$i]'
done
}
echo "# unset in global variable where value: three"
arr_delete_by_content three
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done
echo "# rearrange indices"
arr=( "${arr[#]}" )
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done
delete_value() { # value arrayelements..., returns array decl.
local e val=$1; new=(); shift
for e in "${#}"; do [ "$val" != "$e" ] && new+=("$e"); done
declare -p new|sed 's,^[^=]*=,,'
}
echo "# new array without value: two"
declare -a arr="$(delete_value two "${arr[#]}")"
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done
delete_values() { # arraydecl values..., returns array decl. (keeps indices)
declare -a arr="$1"; local i v; shift
for v in "${#}"; do
for i in ${!arr[*]}; do
[ "$v" = "${arr[$i]}" ] && unset -v 'arr[$i]'
done
done
declare -p arr|sed 's,^[^=]*=,,'
}
echo "# new array without values: one five (keep indices)"
declare -a arr="$(delete_values "$(declare -p arr|sed 's,^[^=]*=,,')" one five)"
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done
# new array without multiple values and rearranged indices is left to the reader
Im having this annoying problem which not even my teacher can solve:/.
I want to fill an array with sum values from 1 to 100 this is my code:
while [ $i -le 100 ]
do
#filling the list with the sums of i at the pos i
sumList[$i]=$(echo $i | sum)
echo $i |sum
echo $sumList[$i]
i=$(($i+1))
done
And for some reason it just fills all spots with the first value (00034 1)
I have no Idea what to do
Here's ShellCheck:
Line 6:
echo $sumList[$i]
^-- SC1087: Use braces when expanding arrays, e.g. ${array[idx]} (or ${var}[.. to quiet).
^-- SC2128: Expanding an array without an index only gives the first element.
And with this fixed:
i=1
while [ $i -le 100 ]
do
#filling the list with the sums of i at the pos i
sumList[$i]=$(echo $i | sum)
echo $i |sum
echo ${sumList[$i]}
i=$(($i+1))
done
You get all the different checksums and block counts that you'd expect:
32802 1
32802 1
00035 1
00035 1
32803 1
32803 1
00036 1
00036 1
32804 1
32804 1
[...]
If you actually examine the output from that script (removing the echo $i |sum line), it should become evident what's happening:
00034 1[0]
00034 1[1]
00034 1[2]
: : :
00034 1[100]
As you can see, the line echo $sumList[$i] is giving you $sumList (which is identical to ${sumList[0]})and $i separately and that's because, as per the bash documentation (my emphasis):
Any element of an array may be referenced using ${name[subscript]}. The braces are required to avoid conflicts ...
So, if you change that to the correct ${sumList[$i]}`, you'll see that you are indeed setting up the array correctly, you just weren't printing it correctly:
00034 1
32802 1
00035 1
: : :
08244 1
And, for what it's worth, there are other facilities in bash that will make your code more succinct, if that is your goal:
for i in {0..100}; do sumList[$i]="$(echo $i | sum)" ; done
IFS=$'\n' ; echo "${sumList[*]}"
I am making a bash script and I have encountered a problem. So let's say I got this
function create_some_array(){
for i in 0 1 2 3 .. 10
do
a[i]=$i
done
}
create_some_array
echo ${a[*]}
Is there any way I can make this work? I have searched quite a lot and nothing I found worked.
I think making the a[] a global variable should work but I can't find something that actually works in my code. Is there any way to return the array from the function to main program?
Thanks in advance
This won't work as expected when there are whitespaces in the arrays:
function create_some_array() {
local -a a=()
for i in $(seq $1 $2); do
a[i]="$i $[$i*$i]"
done
echo ${a[#]}
}
and worse: if you try to get array indices from the outside "a", it turns out to be a scalar:
echo ${!a[#]}
even assignment as an array wont help, as possible quoting is naturally removed by the echo line and evaluation order cannot be manipulated to escape quoting: try
function create_some_array() {
...
echo "${a[#]}"
}
a=($(create_some_array 0 10))
echo ${!a[#]}
Still, printf seems not to help either:
function create_some_array() {
...
printf " \"%s\"" "${a[#]}"
}
seems to produce correct output on one hand:
$ create_some_array 0 3; echo
"0 0" "1 1" "2 4" "3 9"
but assignment doesn't work on the other:
$ b=($(create_some_array 0 3))
$ echo ${!b[#]}
0 1 2 3 4 5 6 7
So my last trick was to do assignment as follows:
$ eval b=("$(create_some_array 0 3)")
$ echo -e "${!b[#]}\n${b[3]}"
0 1 2 3
3 9
Tataaa!
P.S.: printf "%q " "${a[#]}" also works fine...
This works fine as described. The most likely reason it doesn't work in your actual code is because you happen to run it in a subshell:
cat textfile | create_some_array
echo ${a[*]}
would not work, because each element in a pipeline runs in a subshell, and
myvalue=$(create_some_array)
echo ${a[*]}
would not work, since command expansion happens in a subshell.
You can make an array local to a function, and then return it:
function create_some_array(){
local -a a=()
for i in $(seq $1 $2); do
a[i]=$i
done
echo ${a[#]}
}
declare -a a=()
a=$(create_some_array 0 10)
for i in ${a[#]}; do
echo "i = " $i
done
Hi here is my solution:
show(){
local array=()
array+=("hi")
array+=("everything")
array+=("well?")
echo "${array[#]}"
}
for e in $(show);do
echo $e
done
Try this code on: https://www.tutorialspoint.com/execute_bash_online.php
Both these work for me with sh and bash:
arr1=("192.168.3.4" "192.168.3.4" "192.168.3.3")
strArr=$(removeDupes arr1) # strArr is a string
for item in $strArr; do arr2+=("$item"); done # convert it to an array
len2=${#arr2[#]} # get array length
echo "${len2}" # echo length
eval arr3=("$(removeDupes arr1)") # shellcheck does not like this line and won't suppress it but it works
len3=${#arr3[#]} # get array length
echo "${len3}" # echo length
As an aside, the removeDupes function looks like this:
removeDupes() {
arg="$1[#]"
arr=("${!arg}")
len=${#arr[#]}
resultArr=()
# some array manipulation here
echo "${resultArr[#]}"
}
This answer is based on but better explains and simplifies the answers from #Hans and #didierc
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 )) ]}