Can't get array from cmd line argument bash - arrays

For some reason in the code below for loop treats args as array and wc -l can count the lines correctly but I can't get the $(#args[#]) to produce the correct count
function doSomthing() {
local i args a
args=$1;
a=("1" "2" "3" "4");
i=0
echo wc =`wc -l <<< "$args"`;
for arg in $args; do
((i++))
echo "$i"
done;
echo i = $i
echo a = ${#a[#]}
echo args = ${#args[#]}
echo $args
}
The output of this function is
$> doSomthing $'1\n2\n3\n4'
wc = 4
1
2
3
4
i = 4
a = 4
args = 1
1 2 3 4

args is not an array; it is simply a string that contains embedded newlines. That means, if you try to treat it as an array, it will appear as if you defined it as
args=( $'1\n2\n3\4' )
not
args=(1 2 3 4)

Problem solved!
I just needed to put $1 inside parenthesis.
For some reason when I tried it before it did not work but now it does.
Here is the new code:
function doSomthing () {
local i args a;
args=( $1 );
a=("1" "2" "3" "4");
i=0;
echo wc =`wc -l <<< "$args"`;
for arg in $args; do
((i++));
echo "$i";
done;
echo i = $i;
echo a = ${#a[#]};
echo args = ${#args[#]};
echo ${args[#]}
}
And here is the new output:
$> doSomthing $'1\n2\n3\n4'
wc = 1
1
i = 1
a = 4
args = 4
1 2 3 4
Cheers ;-)

Related

How to use an argument in a loop definition

thanks for your help.
What a I doing wrong, here ? I want the numeric answer to be used in a loop but it does not return what I expected :
if [ "$#" -eq 0 ]; then
echo -n "Enter the number: "
read answer
else
answer=( "$#" )
fi
for i in {1..$answer} ; do echo ${i}; done
When executed, I have this:
me#local misc]$ ./test.sh
Enter the number: 5
{1..5}
I expected the echo to return 1 2 3 4 5
You can also do it like:
if [ "$#" -eq 0 ]; then
echo -n "Enter the number: "
read answer
else
answer=( "$#" )
fi
for i in $(seq 1 $answer) ; do echo ${i}; done
With no proposed answer I decided to switch to bash with that syntax that works (though it bothers me)
for (( i=1 ; i<=${answer} ; i++));

Fetching data into an array

I have a file like this below:
-bash-4.2$ cat a1.txt
0 10.95.187.87 5444 up 0.333333 primary 0 false 0
1 10.95.187.88 5444 up 0.333333 standby 1 true 0
2 10.95.187.89 5444 up 0.333333 standby 0 false 0
I want to fetch the data from the above file into a 2D array.
Can you please help me with a suitable way to put into an array.
Also post putting we need put a condition to check whether the value in the 4th column is UP or DOWN. If it's UP then OK, if its down then below command needs to be executed.
-bash-4.2$ pcp_attach_node -w -U pcpuser -h localhost -p 9898 0
(The value at the end is getting fetched from the 1st column.
You could try something like that:
while read -r line; do
declare -a array=( $line ) # use IFS
echo "${array[0]}"
echo "${array[1]}" # and so on
if [[ "$array[3]" ]]; then
echo execute command...
fi
done < a1.txt
Or:
while read -r -a array; do
if [[ "$array[3]" ]]; then
echo execute command...
fi
done < a1.txt
This works only if field are space separated (any kind of space).
You could probably mix that with regexp if you need more precise control of the format.
Firstly, I don't think you can have 2D arrays in bash. But you can however store lines into a 1-D array.
Here is a script ,parse1a.sh, to demonstrate emulation of 2D arrays for the type of data you included:
#!/bin/bash
function get_element () {
line=${ARRAY[$1]}
echo $line | awk "{print \$$(($2+1))}" #+1 since awk is one-based
}
function set_element () {
line=${ARRAY[$1]}
declare -a SUBARRAY=($line)
SUBARRAY[$(($2))]=$3
ARRAY[$1]="${SUBARRAY[#]}"
}
ARRAY=()
while IFS='' read -r line || [[ -n "$line" ]]; do
#echo $line
ARRAY+=("$line")
done < "$1"
echo "Full array contents printout:"
printf "%s\n" "${ARRAY[#]}" # Full array contents printout.
for line in "${ARRAY[#]}"; do
#echo $line
if [ "$(echo $line | awk '{print $4}')" == "down" ]; then
echo "Replace this with what to do for down"
else
echo "...and any action for up - if required"
fi
done
echo "Element access of [2,3]:"
echo "get_element 2 3 : "
get_element 2 3
echo "set_element 2 3 left: "
set_element 2 3 left
echo "get_element 2 3 : "
get_element 2 3
echo "Full array contents printout:"
printf "%s\n" "${ARRAY[#]}" # Full array contents printout.
It can be executed by:
./parsea1 a1.txt
Hope this is close to what you are looking for. Note that this code will loose all indenting spaces during manipulation, but a formatted update of the lines could solve that.

Store splitting words into array while reading a file bash

I have a file that contains multiple words with commas. I want to read a file and store these words into array. In this file, first line is header, the other lines are datum. But some datum are null.So, this null characters need to be 0 For instance;
H1;H2;H3;H4
12;23;33;44
44;;7;8
13;;;9
So I want to skip first line and put datum into 4 array=>
H1 array= 12;44;13
H2 array= 23;0;0
H3 array= 33;7;0
H4 array= 44;8;9
So my code is like that:
array=()
awk 'NR>1' $filename2 | while read line
do
cntr=0
IFS=";"
for i in $line; do
if [ -z $i ]; then array[cntr]=0;
else array[cntr]=$i;
fi
cntr=$[$cntr +1]
done
h1array+=("${array[0]}")
h2array+=("${array[1]}")
h3array+=("${array[2]}")
h4array+=("${array[3]}")
done
for ((i=0;i<3;i++)); do
echo "${h1array[$i]}"
done
for ((i=0;i<3;i++)); do
echo "${h2array[$i]}"
done
for ((i=0;i<3;i++)); do
echo "${h3array[$i]}"
done
for ((i=0;i<3;i++)); do
echo "${h4array[$i]}"
done
So,it prints null in terminal. How can I do this? Thank you
Is this what you are after?
#!/bin/bash
while read -r line; do
[[ "${line}" == *';'* ]] || continue
IFS=';' read -r h1 h2 h3 h4 <<< "${line}"
h1array+=("${h1:-0}")
h2array+=("${h2:-0}")
h3array+=("${h3:-0}")
h4array+=("${h4:-0}")
done < <(tail -n +2 input.txt)
echo "h1array = ${h1array[#]}"
echo "h2array = ${h2array[#]}"
echo "h3array = ${h3array[#]}"
echo "h4array = ${h4array[#]}"
.
$ ./t.sh
h1array = 12 44 13
h2array = 23 0 0
h3array = 33 7 0
h4array = 44 8 9

In array operator in bash

Is there a way to test whether an array contains a specified element?
e.g., something like:
array=(one two three)
if [ "one" in ${array} ]; then
...
fi
A for loop will do the trick.
array=(one two three)
for i in "${array[#]}"; do
if [[ "$i" = "one" ]]; then
...
break
fi
done
Try this:
array=(one two three)
if [[ "${array[*]}" =~ "one" ]]; then
echo "'one' is found"
fi
I got an function 'contains' in my .bashrc-file:
contains ()
{
param=$1;
shift;
for elem in "$#";
do
[[ "$param" = "$elem" ]] && return 0;
done;
return 1
}
It works well with an array:
contains on $array && echo hit || echo miss
miss
contains one $array && echo hit || echo miss
hit
contains onex $array && echo hit || echo miss
miss
But doesn't need an array:
contains one four two one zero && echo hit || echo miss
hit
I like using grep for this:
if echo ${array[#]} | grep -qw one; then
# "one" is in the array
...
fi
(Note that both -q and -w are non-standard options to grep: -w tells it to work on whole words only, and -q ("quiet") suppresses all output.)
array="one two three"
if [ $(echo "$array" | grep one | wc -l) -gt 0 ] ;
then echo yes;
fi
If that's ugly, you could hide it away in a function.
if you just want to check whether an element is in array, another approach
case "${array[#]/one/}" in
"${array[#]}" ) echo "not in there";;
*) echo "found ";;
esac
In_array() {
local NEEDLE="$1"
local ELEMENT
shift
for ELEMENT; do
if [ "$ELEMENT" == "$NEEDLE" ]; then
return 0
fi
done
return 1
}
declare -a ARRAY=( "elem1" "elem2" "elem3" )
if In_array "elem1" "${ARRAY[#]}"; then
...
A nice and elegant version of the above.
in_array() {
local needle=$1 el
shift
for el in "$#"; do
if [ "$el" = "$needle" ]; then
return 0
fi
done
return 1
}
if in_array 1 1 2 3; then
echo true
else
echo false
fi
# alternatively
a=(1 2 3)
if in_array 1 "${a[#]}"; then
...
OPTIONS=('-q','-Q','-s','-S')
find="$(grep "\-q" <<< "${OPTIONS[#]}")"
if [ "$find" = "${OPTIONS[#]}" ];
then
echo "arr contains -q"
fi

shell script function and for loop

the following function in shell script only loops through the first element of the array when called on an array, what's wrong?
#!/bin/sh
in_array(){
a=$2
echo ${#a[#]}
for (( i = 0; i < ${#a[#]} ; i++ ))
do
echo ${a[$i]}
if [ $1 = ${a[$i]} ]
then
return 1
fi
done
return 0
}
exclude_dirs=( aaa bbb )
echo ${#exclude_dirs[#]}
in_array home $exclude_dirs
There are 2 problems. The first is that sh does not support arrays, so your shebang should be changed to a shell that does. (eg, #!/bin/bash). The second problem is more substantial. Arrays are not first class objects in bash (they may be in other shells but I'm going to answer the question for bash, since sh is often bash in many Linux distros and I'm using my crystal ball to determine that you meant bash when you said #!/bin/sh). You can get what you want by using eval. Change your function to something like this:
in_array(){
a=$2
eval "for i in \${$a[#]}; do
echo \$i
test $1 = \$i && return 1
done"
return 0
}
and invoke it without a '$'.
in_array home exclude_dirs
Also, I would strongly recommend inverting the return values. (return 0 if $1 appears in the array, and return 1 if it does not). Either that, or change the function name to "not_it_array". That will allow you to write things like:
if in_array home exclude_dirs; then echo home is excluded; fi
(or use a short circuiting &&). Remember that in sh, 0 is success, and non-zero is failure.
Of course, it would be easier to pass the array by passing all of the values rather than passing the name:
#!/bin/bash
in_array(){
el=$1
shift
while test $# -gt 0; do
test $el = $1 && return 0
shift
done
return 1
}
exclude_dirs=( aaa home bbb )
in_array home ${exclude_dirs[#]} && echo home is excluded
William is right that you can't use arrays in Bourne Shell, but you shouldn't be using eval either. To avoid it you could just simplify your parameter structure:
#!/bin/sh
in_array(){
search_path="$1"
shift
while [ -n "${1+defined}" ]
do
if [ "$1" = "$search_path" ]
then
return 0
fi
shift
done
return 1
}
in_array foo aaa bbb && echo fail test 1
in_array foo foo bar || echo fail test 2
in_array foo bar foo || echo fail test 3
in_array foo foobar && echo fail test 4
in_array foo barfoo && echo fail test 5
in_array foo "foo bar" && echo fail test 6
in_array "foo bar" "foo bar" "baz ban" || echo fail test 7
true

Resources