Odd results when parsing array in bash - arrays

I'm building a bash script that gets information from GeekTool and parses the results. The so far the script looks like this:
1 #!/bin/bash
2
3 clear
4
5 geeklets=`osascript <<TEXT
6 Tell Application "GeekTool Helper"
7 geeklets
8 End Tell
9 TEXT`
10
11 IFS="," read -a geekletArray <<< "$geeklets"
12
13 echo "First Element in Array:"
14 echo ${geekletArray[0]}
15
16 echo ""
17
18 echo "All Array Elements:"
19 for element in ${geekletArray[#]}
20 do
21 echo $element
22 done
At line 14 I echo out the first element of the array which returns:
shell geeklet id 49610161-0A3C-49C6-9626-694370DE3101
but on the first iteration of the loop that steps through the array (line 21), the first element array is returned like this:
shell
geeklet
id
49610161-0A3C-49C6-9626-694370DE3101
In the loop the elements are returned in a newline delimited list.
Why is there a difference? Also, if I wanted to grab just the id value of the array (e.g. 49610161-0A3C-49C6-9626-694370DE3101), how would I go about it?
Thanks!

for element in "${geekletArray[#]}"
do
echo "$element"
done
Judicious use of quotes will protect you from undesired word splitting.

Quoting of arrays can be confusing. You can sidestep the issue completely by iterating over the array indices instead.
There are lots of ways to pull out the id value from the resulting string. I am assuming it will always be the last part of a space-delimited string. As such, I am using a bash parameter expansion to effectively remove all occurrences of the pattern *
for index in ${!geekletArray[#]}
do
echo "${geekletArray[$index]}" # entire element
echo "${geekletArray[$index]##* }" # id value only
done

Related

bash shell pull all values from array in random order. Pull each value only once

I need to pull the values from an array in a random order. It shouldn't pull the same value twice.
R=$(($RANDOM%5))
mixarray=("I" "like" "to" "play" "games")
echo ${mixarray[$R]}
I'm not sure what to do after the code above. I thought of putting the first pulled value into another array, and then nesting it all in a loop that checks that second array so it doesn't pull the same value twice from the first array. After many attempts, I just can't get the syntax right.
The output should be something like:
to
like
I
play
games
Thanks,
Would you please try the following:
#!/bin/bash
mixarray=("I" "like" "to" "play" "games")
mapfile -t result < <(for (( i = 0; i < ${#mixarray[#]}; i++ )); do
echo -e "${RANDOM}\t${i}"
done | sort -nk1,1 | cut -f2 | while IFS= read -r n; do
echo "${mixarray[$n]}"
done)
echo "${result[*]}"
First, it prints a random number and an index starting with 0 side by side.
The procedure above is repeated as much as the length of mixarray.
The output will look like:
13959 0
6416 1
6038 2
492 3
19893 4
Then the table is sorted with the 1st field:
492 3
6038 2
6416 1
13959 0
19893 4
Now the indices in the 2nd field are randomized. The field is extracted with
the cut command.
Next rearrange the elements of mixarray using the randomized index.
Finally the array result is assigned with the output and printed out.

Populate array in a for loop

I have an array of strings to pass through a script. The script is well-behaved, and will return error code 0 if the string "passes" and non-zero if it "fails." If the string passes, it should be included in a final array to be output or written to file or etc.
The problem I'm having is that the only item ending up in my final array is the first "passing" string.
#!/bin/bash
# validator.sh
if [[ $1 -le 10 ]]; then
exit 0
else
exit 1
fi
#!/bin/bash
# main.sh
numbers=(2 4 6 8 10 12 14 16)
keep=()
for n in ${numbers[#]}; do
if ./validator.sh $n; then
keep+=("$n")
fi
done
echo $keep
Running main.sh produces:
$ ./main.sh
2
but I expect 2 4 6 8 10
Unless you meant keep to be an array of matching elements, change:
keep+=("$n")
to
keep="$keep $n"
That would work with any Bourne compatible shell and is therefore better, if you're looking for BASH specific solution, the below will also work:
keep+="${n} "
If you DO want it to be an array, then in order to output all elements, you can use:
echo ${keep[#]}
As noted by #Jetchisel and #kamilCuk in the comments.
Since you wrote you want to output all elements or save them to a file, I had assumed you don't actually need an array here but perhaps you plan to use this data in other ways later:)

How to get user input as number and echo the stored array value of that number in bash scripting

I have wrote a script that throws the output of running node processes with the cwd of that process and I store the value in an array using for loop and do echo that array.
How can I able to get the user enter the index of array regarding the output that the script throws and show the output against that input generated by user
Example Myscript
array=$(netstat -nlp | grep node)
for i in ${array[*]}
do
echo $i
done
output is something like that
1056
2064
3024
I want something more advance. I want to take input from user like
Enter the regarding index from above list = 1
And lets suppose user enter 1
Then next output should be
Your selected value is 2064
Is it possible in bash
First, you're not actually using an array, you are storing a plain string in the variable "array". The string contains words separated by whitespace, so when you supply the variable in the for statement, the unquoted value is subject to Word Splitting
You need to use the array syntax for setting the array:
array=( $(netstat -nlp | grep node) )
However, the unquoted command substitution still exposes you to Filename Expansion. The best way to store the lines of a command into an array is to use the mapfile command with a process substitution:
mapfile -t array < <(netstat -nlp | grep node)
And in the for loop, make sure you quote all the variables and use index #
for i in "${array[#]}"; do
echo "$i"
done
Notes:
arrays created with mapfile will start at index 0, so be careful of off-by-one errors
I don't know how variables are implemented in bash, but there is this oddity:
if you refer to the array without an index, you'll get the first element:
array=( "hello" "world" )
echo "$array" # ==> hello
If you refer to a plain variable with array syntax and index zero, you'll get the value:
var=1234
echo "${var[0]}" # ==> 1234

bash function returns with changed indexes

I have following code:
#!/bin/bash
function getSlots {
declare -A slots
for index in `seq 7 10`;
do
slots[$index]=$index
done
echo ${slots[#]}
}
slots=($(getSlots))
for i in ${!slots[#]};
do
echo "$i ${slots[$i]}"
done
When I run, my output is this:
0 10
1 7
2 8
3 9
Why do indexes change when I call the function?
arr=(...) re-indexes the array.
With your current approach, you cannot preserve the indexes as any information about them is lost as soon as you leave the function (you just echo the values).
You can use nameref (requires bash 4.3 or later) to modify the supplied array directly and since you are using only numbers as indexes, regular array will suffice:
#!/usr/bin/env bash
function get_slots {
local index # makes index local
local -n _arr=$1 # _arr points to the supplied array
_arr=() # empties the array
for index in {7..10}; do
_arr[index]=$index # assigns new values to the array
done
}
get_slots slots
for i in "${!slots[#]}"; do
echo "$i ${slots[$i]}"
done
Because the echo ${slots[#]} at the end of the function getSlots expands to echo 10 7 8 9 and that output is what you are assigning to the array slots by doing:
slots=($(getSlots))
Another interesting question would be why echo ${slots[#]} expands to echo 10 7 8 9 and not to echo 7 8 9 10.
That is because the slots inside getSlots is declared as an associative array, not an array, i.e.:
declare -A slots
Replace the line above with:
declare -a slots
and you will get the following output:
0 7
1 8
2 9
3 10

Bash Array - Cant get input from a loop in to my array correctly

I run this script as
script.sh 'abcdefghijk123456789!##$%^&' 'aaaa4444ged###'
it should be able to produce an array containing something like
1 1 1 1 15 15 15 15 7 5 23 23 23
with 12 indexes 0 - 11, but what i get is
1 1 1 1 1 5 1 5 1 5 1 5 7 5 2 3 2 3 2 3
with 20 indexes.
I want to populate a array with base numbers for a given char.
So i have a list of chars that we will be using in $startstring ie.
abcc5678% we can say that every char in $startstring is = to one char in the
$charset ie. abcd5678!%. This code finds what each $startstring char is equal
to $charset's index number. That information is what i am trying to capture in a
array. Mostly it works except a bug where instead of the whole number 10 getting
stored in decoded[1] what happens is the number 10 is split in to 1 and 0 and
then both are put in under separate indexes. So instead of a "1 10 1" and 3
indexes i end up with 4 indexes with "1 1 0 1". Im sure im just handling my
variables the wrong way but i searched and searched and now my brain is gonna
explode so i came here for some relief. or hope of it anyway. Can someone tell
me the proper way to insert digits in to this decoded[] array?
#!/bin/bash
#declare -i decoded
charset=$1
startstring=$2
start=$((${#charset}-1))
echo "Setting up CharMap to CharSet"
for i in $(eval echo {0..$start})
do
echo $i " = " ${charset:$i:1}
done
echo "Proving Chars Were Mapped Correctly."
start2=$((${#startstring}))
start3=$((${#charset}-1))
for i in $(eval echo {0..$start2})
do
for p in $(eval echo {0..$start3})
do
if [ "${startstring:$i:1}" == "${charset:$p:1}" ]
then
echo "found that" ${startstring:$i:1}"=" $p 'from the charmap.'
decoded+=$p #<--### I DONT THINK THIS IS WHAT I NEED ###
fi
done
done
##################Just trying to print my new array#########################
start4=$((${#decoded}-1))
echo 'Testing the array $decoded'
echo 'the var start4(length of $decoded) = '$start4
echo 'this number should equal ----------> '$start2
echo 'Printing out the $decoded array in a for loop'
for c in $(eval echo {0..$start4})
do
echo ${decoded[$c]} ###DOESNT WORK LIKE I THOUGHT# also tried echo ${decode:$c:1}
done
decoded+=$p appends $p as a string, not as an array entry. Essentially, you're creating the string "11111515151575232323" by appending all the index numbers together. (Actually, I get "00001414141464322222227" from your example, because of index bound problems. I'll let you worry about that...)
To store the decoded values as an array, set decoded to an empty array before the loop, and then use decoded+=("$p") to add $p as an element:
decoded=() # create an empty array
for i in $(eval echo {0..$start2})
do
for p in $(eval echo {0..$start3})
do
if [ "${startstring:$i:1}" == "${charset:$p:1}" ]
then
echo ${startstring:$i:1} "=" $p
decoded+=("$p") # append $p as a new array element
fi
done
done
Then, to get the size of the array (rather than a string length), use ${#decoded[#]}:
start4=$((${#decoded[#]}-1))

Resources