Bash function, array howto? - arrays

I'm struggling to wrap my head around Bash arrays, in particular I have this function where I need to load an array; What I have written is this:
function list_files() {
for f in *; do
[[ -e $f ]] || continue
done
}
function list_array() {
array=list_files
number=0
for items in "${array[#]}"
do
let "number +=1"
echo -e "\033[1m$number\033[0m) $items"
tput sgr0
let "number -=$(echo "${#array[*]}")"
done
}
The problem here is that the function only works once, however I need to run this several times in the script. I am unsure how to go about doing this. Either I have to empty and reload the array every time the function is invoked, or I have to supply a different array name in the function parameter (list_array myarrayname in stead of just list_array). However I have no idea how to accomplish either of these tasks, or if they are possible/feasible.
Any help would be very welcomed!

A bit unclear what you are trying to achieve; perhaps you can find some inspiration from below:
#!/bin/bash
list_files() {
number=0
for f in *
do
if [[ -f $f ]]
then
number=$((number+=1))
echo $f, $number
fi
done
}
list_files_array() {
array=($1/*)
number=0
for item in ${array[#]}
do
if [[ -f $item ]]
then
number=$((number+=1))
echo $item, $number
fi
done
}
list_files $(pwd)
list_files_array $(pwd)

Related

String matching not working in bash script

This is weird.. mainly because it worked last night. I have a script that is passed in an expression that is supposed to match file names in an array. I'm not allowed to use find however (it is for an assignment).
For example if the user passes in the argument '*.c', and I have these files to check it against:
~/dir/text1.txt
~/dir/no.c
~/dir/no.sh
I do the comparison in a function like so:
process ()
{
local new_cur=()
for i in "${files[#]}"; do
if [[ "$i" == "$1" ]]; then
new_cur+=("$i")
fi
done
}
I then call this in the bash script like this, where arguments[1] is = to '*.c':
process "${arguments[1]}"
Inside my process function, I checked and the string *.c is being passed in, and the files are being passed in. However it is not passing the if statement within my function.
Note that this also does not work for even simple strings without wildcards.
Edit: I have also looked up common solutions, and for whatever reason none work. Here is what I have tried:
if [ "$i" == "$1" ]
if [ "$1" == "$i" ]
if [ "$i" = "$1" ]
if [ "$1" = "$i" ]
if [[ "$1" = "$i" ]]
if [[ "$i" = "$1" ]]
if [[ "$1" == "$i" ]]
if [[ "$i" == *"$1"* ]]
So far none of the above have worked.
First declare the glob as a variable, then use the variable in the test. So, declare:
var="$1" then [ $i == $var ]
The reason for this is to force bash to expand the wildcard before the test.

grep through array in bash

Is it possible to grep an array in bash? Basically I have two arrays, one array I want to loop through and the other array I want to check to see if each string is in the other array or not.
Rough example:
for CLIENTS in ${LIST[#]}; do
#lost as to best way to grep a array with out putting the values in a file.
grep ${server_client_list[#]}
done
You can use grep -f using process substitution:
grep -Ff <(printf "%s\n" "${LIST[#]}") <(printf "%s\n" "${server_client_list[#]}")
Bash provides substring matching. This eliminates the need to spawn external processes and subshells. Simply use:
for CLIENTS in ${LIST[#]}; do
if [[ "${server_client_list[#]}" =~ "$CLIENTS" ]]; then
echo "CLIENTS ($CLIENTS) is in server_client_list"
fi
done
Note: this works for bash, not sh.
You could use the following function :
function IsInList {
local i
for i in "${#:2}"; do
[ "$i" = "$1" ] && return 0
done
return 1
}
for CLIENT in "${LIST[#]}"; do
IsInList "$CLIENT" "${server_client_list[#]}" && echo "It's in the list!"
done
Note that the quotes around ${LIST[#]} and ${server_client_list[#]} are very important because they guarantee the array expansion to expand the way you want them to, instead of having a bad surprise when a space make its way into one of those array values.
Credits : I took this function from patrik's answer in this post when I needed something like that.
Edit :
If you were looking for something with regexp capabilities, you could also use the following :
function IsInList {
local i
for i in "${#:2}"; do
expr match "$i" "$1" && return 0
done
return 1
}

Array returns false when i know it should be true

I know that this particular volume should be coming out as true but it keeps returning as false. Can anyone see what is wrong with it? My guess is it has something to do with the if statement but i have no idea what. the "3D/Gather" are 2 seperate strings but i want it so that if they are together then it returns true. This is the start of what is going to be quite a long script so i want to make it a function that i can call upon later on. Thanks in advance for your help.
#!/bin/bash
session_path=$PWD/testSess/3DBPvol.proc
outPath=""
sess=$session_path
{
ThreeD="false"
while read line; do
IFS=" "
arr=$(echo ${line})
unset IFS
for i in ${arr[#]} ; do
if [[ "$i" =~ "3D" ]] && [[ "$i" =~ "Gather" ]]; then
ThreeD="true"
fi
done
done < "$sess"
echo "Is 3D? $ThreeD"
}
arr=$(echo ${line})
This doesn't create an array, you'd need extra parens:
arr=($(echo ${line}))
But you don't actually need the echo, this should be enough:
arr=(${line})
So you want to see if the file under /proc contains "3D Gather"? You can do that simply with a grep:
#!/bin/bash
session_path=$PWD/testSess/3DBPvol.proc
grep -q '3D Gather' "$session_path" && ThreeD=true || ThreeD=false
echo "Is 3D? $ThreeD"
It seems you don't need to break the line into a BASH array. Consider this rafactored script that does the same job but with a lot less code:
ThreeD="false"
while read line; do
[[ "$line" == *"3D Gather"* ]] && ThreeD="true" && break
done < "$sess"
echo "Is 3D? $ThreeD"

BASH: how to store subshell variables into an array?

I have array1, array2 and a function.
I am trying in a for j=0 to ARRAY_SIZE loop to get the data from array2[j], pass it to a function and the returning output store it in array1[j].
Below is the part of the code that I am working on:
exec 3>&1
${ppart_block_fstype[$i]}=_ppart_block_fstype < <(
for i in $(eval echo {0..$ARRAY_END})
do
if [[ ppart_block_alloc[$i] -eq "ALLOC" ]]
then
printf "%s\n" "${ppart_block_num[$i]}" >&3
fi
done)
exec 3>&-
_ppart_block_fstype is the function that I have previously defined and will return an output that I will store in array ppart_block_fstype.
The problem of above function is that is using some "heavy tools", hence is not really possible to invoke it at every loop cycle.
This! was a good starting point, but I am stuck on how to make $i visible out of the subshell, and I am also not sure if I am invoking < <( )* in the correct way.

BASH: Best way to set variable from array

Bash 4 on Linux ~
I have an array of possible values. I must restrict user input to these values.
Arr=(hello kitty goodbye quick fox)
User supplies value as argument to script:
bash myscript.sh -b var
Currently, I'm trying the following:
function func_exists () {
_var="$1"
for i in ${Arr[#]}
do
if [ "$i" == "$_var" ]
then
echo hooray for "$_var"
return 1
fi
done
return 0
}
func_exists $var
if [ $? -ne 1 ];then
echo "Not a permitted value."
func_help
exit $E_OPTERROR
fi
Seems to work fine, are there better methods for testing user input against an array of allowed values?
UPDATE: I like John K's answer ...can someone clarify the use of $#? I understand that this represents all positional parameters -- so we shift the first param off the stack and $# now represents all remaining params, those being the passed array ...is that correct? I hate blindly using code without understanding ...even if it works!
Your solution is what I'd do. Maybe using a few more shell-isms, such as returning 0 for success and non-0 for failure like UNIX commands do in general.
# Tests if $1 is in the array ($2 $3 $4 ...).
is_in() {
value=$1
shift
for i in "$#"; do
[[ $i == $value ]] && return 0
done
return 1
}
if ! is_in "$var" "${Arr[#]}"; then
echo "Not a permitted value." >&2
func_help
exit $E_OPTERROR
fi
Careful use of double quotes makes sure this will work even if the individual array entries contain spaces, which is allowed. This is a two element array: list=('hello world' 'foo bar').
Another solution. is_in is just a variable:
Arr=(hello kitty goodbye quick fox)
var='quick'
string=" ${Arr[*]} " # array to string, framed with blanks
is_in=1 # false
# try to delete the variable inside the string; true if length differ
[ "$string" != "${string/ ${var} /}" ] && is_in=0
echo -e "$is_in"
function func_exists () {
case "$1"
in
hello)
kitty)
goodbye)
quick)
fox)
return 1;;
*)
return 0;;
esac
}

Resources