bash script to dynamically display array element based on user selection(s) - arrays

i have to task to write a bash script which can let user to choose which array element(s) to display.
for example, the array have these element
ARRAY=(zero one two three four five six)
i want the script to be able to print out the element after user enter the array index
let say the user entered(in one line) : 1 2 6 5
then the output will be : one two six five
the index entered can be single(input: 0 output: zero) or multiple(such as above) and the output will be correspond to the index(es) entered
any help is much appreciated.
thanks

ARRAY=(zero one two three four five six)
for i in $#; do
echo -n ${ARRAY[$i]}
done
echo
Then call this script like this:
script.sh 1 2 6 5
it will print:
one two six five

You would want to use associative arrays for a generic solution, i.e. assuming your keys aren't just integers:
# syntax for declaring an associative array
declare -A myArray
# to quickly assign key value store
myArray=([1]=one [2]=two [3]=three)
# or to assign them one by one
myArray[4]='four'
# you can use strings as key values
myArray[five]=5
# Using them is straightforward
echo ${myArray[3]} # three
# Assuming input in $#
for i in 1 2 3 4 five; do
echo -n ${myArray[$i]}
done
# one two three four 5
Also, make sure you're using Bash4 (version 3 doesn't support it). See this answer and comment for more details.

Related

Bash - fastest way to do whole string matching over array elements?

I have bash array (called tenantlist_array below) populated with elements with the following format:
{3 characters}-{3-5 characters}{3-5 digits}-{2 chars}{1-2 digits}.
Example:
abc-hac101-bb0
xyz-b2blo97250-aa99
abc-b2b9912-xy00
fff-hac101-g3
Array elements are unique. Please notice the hyphen, it is part of every array element.
I need to check if the supplied string (used in the below example as a variable tenant) produces a full match with any array element - because array elements are unique, the first match is sufficient.
I am iterating over array elements using the simple code:
tenant="$1"
for k in "${tenantlist_array[#]}"; do
result=$(grep -x -- "$tenant" <<<"$k")
if [[ $result ]]; then
break
fi
done
Please note - I need to have a full string match - if, for example, the string I am searching is hac101 it must not match any array element even if can be a substring if an array element.
In other words, only the full string abc-hac101-bb0 must produce the match with the first element. Strings abc, abc-hac, b2b, 99, - must not produce the match. That's why -x parameter is with the grep call.
Now, the above code works, but I find it quite slow. I've run it with the array having 193 elements and on an ordinary notebook it takes almost 90 seconds to iterate over the array elements:
real 1m2.541s
user 0m0.500s
sys 0m24.063s
And with the 385 elements in the array, time is following:
real 2m8.618s
user 0m0.906s
sys 0m48.094s
So my question - is there a faster way to do it?
Without running any loop you can do this using glob:
tenant="$1"
[[ $(printf '\3%s\3' "${tenantlist_array[#]}") == *$'\3'"$tenant"$'\3'* ]] &&
echo "ok" || echo "no"
In printf we place a control character \3 around each element and while comparing we make sure to place \3 before & after search key.
Thanks to #arco444, the solution is astonishingly simple:
tenant="$1"
for k in "${tenantlist_array[#]}"; do
if [[ $k = "$tenant" ]]; then
result="$k"
break
fi
done
And the seed difference for the 385 member array:
real 0m0.007s
user 0m0.000s
sys 0m0.000s
Thousand times faster.
This gives an idea of how wasteful is calling grep, which needs to be avoided, if possible.
This is an alternative way of using grep that actually uses grep at most of its power.
The code to "format" the array could be completely removed just appending a \n at the end of each uuid string when creating the array the first time.
This code would also degrade much slower with the length of the strings that are compared and with the length of the array.
tenant="$1"
formatted_array=""
for k in "${tenantlist_array[#]}"; do
formatted_array="$formatted_array $i\n"
done
result=$(echo -e "$formatted_array" | grep $tenant)

How can I qsub a bash script in a job array varying its parameters stored in a array in bash

I have a script which I want to execute with several different arguments, I've got an array that contains all the parameter(argument) combinations ${array[i]}.
I want to be able to submit a job array using all the different argument stored in the array:
arr_length=${#submittions[#]}
qsub -t 1-$arr_length myscript <*>
*Here, I want to use the values of -t to go through my array and use the different parameters stored in it here, I don't know is it is possible.
I have read that there is a built in variable $SGE_TASK_ID.
The array contain from two to seven file paths separated by one space and an amount of arr_length elements in the array. which will be the arguments for the python script.
${!array[#]} never contains the values of the elements in the array. The contain only the indices. For the array elements use "${array[#]}" in your script as
qsub -t 1-${arr_length} myscript "${array[#]}"
E.g.
array=('foo' 'bar' 'dude')
printf '%s\n' "${!array[#]}"
0
1
2
and see the output of
printf '%s\n' "${array[#]}"

Change values of array that is a copy of argument array

I am trying to change the values inside an array that is a copy of arguments array ("$#"). Let's say I execute the script as follows: $ sh script.sh 1 2 3
This is the script:
list="$#"
echo ${list[*]}
list[1]=4
echo ${list[*]}
Expected output:
1 2 3
1 4 3
What I actually get:
1 2 3
1 2 3 4
Any idea what causes this behavior and how can I fix it?
list="$#" sets list to a plain variable, not an array. Use list=("$#") to store it as an array instead. BTW, you should generally use "${list[#]}" to get the elements of an array, instead of ${list[*]} to avoid problems with whitespace in elements, wildcards getting expanded to lists of matching files, etc.
In general, this is the proper way to copy an array:
copyarray=("${oldarray[#]}")
Just found this answer. Command line arguments array is not quite a normal array and has to be converted to an actual array first as follows:
list=("$#")

Practical use of bash array

After reading up on how to initialize arrays in Bash, and seeing some basic examples put forward in blogs, there remains some uncertainties on its practical use. An interesting example perhaps would be to sort in ascending order -- list countries from A to Z in random order, one for each letter.
But in the real world, how is a Bash array applied? What is it applied to? What is the common use case for arrays? This is one area I am hoping to be familiar with. Any champions in the use of bash arrays? Please provide your example.
There are a few cases where I like to use arrays in Bash.
When I need to store a collections of strings that may contain spaces or $IFS characters.
declare -a MYARRAY=(
"This is a sentence."
"I like turtles."
"This is a test."
)
for item in "${MYARRAY[#]}"; do
echo "$item" $(echo "$item" | wc -w) words.
done
This is a sentence. 4 words.
I like turtles. 3 words.
This is a test. 4 words.
When I want to store key/value pairs, for example, short names mapped to long descriptions.
declare -A NEWARRAY=(
["sentence"]="This is a sentence."
["turtles"]="I like turtles."
["test"]="This is a test."
)
echo ${NEWARRAY["turtles"]}
echo ${NEWARRAY["test"]}
I like turtles.
This is a test.
Even if we're just storing single "word" items or numbers, arrays make it easy to count and slice our data.
# Count items in array.
$ echo "${#MYARRAY[#]}"
3
# Show indexes of array.
$ echo "${!MYARRAY[#]}"
0 1 2
# Show indexes/keys of associative array.
$ echo "${!NEWARRAY[#]}"
turtles test sentence
# Show only the second through third elements in the array.
$ echo "${MYARRAY[#]:1:2}"
I like turtles. This is a test.
Read more about Bash arrays here. Note that only Bash 4.0+ supports every operation I've listed (associative arrays, for example), but the link shows which versions introduced what.

How can I split bash CLI arguments into two separate arrays for later usage?

New to StackOverflow and new to bash scripting. I have a shell script that is attempting to do the following:
cd into a directory on a remote machine. Assume I have already established a successful SSH connection.
Save the email addresses from the command line input (these could range from 1 to X number of email addresses entered) into an array called 'emails'
Save the brand IDs (integers) from the command line input (these could range from 1 to X number of brand IDs entered) into an array called 'brands'
Use nested for loops to iterate over the 'emails' and 'brands' arrays and add each email address to each brand via add.py
I am running into trouble splitting up and saving data into each array, because I do not know where the command line indices of the emails will stop, and where the indices of the brands will begin. Is there any way I can accomplish this?
command line input I expect to look as follows:
me#some-remote-machine:~$ bash script.sh person1#gmail.com person2#gmail.com person3#gmail.com ... personX#gmail.com brand1 brand2 brand3 ... brandX
The contents of script.sh look like this:
#!/bin/bash
cd some/directory
emails= ???
brands= ???
for i in $emails
do
for a in $brands
do
python test.py add --email=$i --brand_id=$a --grant=manage
done
done
Thank you in advance, and please let me know if I can clarify or provide more information.
Use a sentinel argument that cannot possibly be a valid e-mail address. For example:
$ bash script.sh person1#gmail.com person2#gmail.com '***' brand1 brand2 brand3
Then in a loop, you can read arguments until you reach the non-email; everything after that is a brand.
#!/bin/bash
cd some/directory
while [[ $1 != '***' ]]; do
emails+=("$1")
shift
done
shift # Ignore the sentinal
brands=( "$#" ) # What's left
for i in "${emails[#]}"
do
for a in "${brands[#]}"
do
python test.py add --email="$i" --brand_id="$a" --grant=manage
done
done
If you can't modify the arguments that will be passed to script.sh, then perhaps you can distinguish between an address and a brand by the presence or absence of a #:
while [[ $1 = *#* ]]; do
emails+=("$1")
shift
done
brands=("$#")
I'm assuming that the number of addresses and brands are independent. Otherwise, you can simply look at the total number of arguments $#. Say there are N of each. Then
emails=( "${#:1:$#/2}" ) # First half
brands=( "${#:$#/2+1}" ) # Second half

Resources