Bash, compare string to arrayvalue - arrays

i try to match a parameter with some array content. At the if clauses should be true, but it wont be.
At the output before compare i got this:
VAL: drei_01 AND: drei
#!/bin/bash
array=( null_01 eins_01 zwei_01 drei_01 vier_01 )
lookarr() {
maxc=${#array[#]}
mbool=0
for((i=0; i<$maxc; i++))
do
val=${array[$i]}
echo "VAL: $val AND: $1"
if [[ $1 == *" $val "* ]]; then
echo "TESTENTRY1"
#do something
mbool=1
break
fi
done
if [[ $mbool -eq 0 ]]; then
echo "TESTENTRY2"
#do something else
fi
}
lookarr drei
thanks

Your if statement isn't matching because it is back-to-front and has extra spaces. For drei to match drei_01 you can replace your if statement with:
if [[ "$val" == *"$1"* ]]; then

Related

Bash - How to check if value doesn't exist in array?

Folks,
I have an array, ex,
declare -a arr=("crm" "hr" "pos")
I need to output error if the passed value doesn't exist in this array
I'm trying this use below snippet but it prints "No match found" for any value
match=0
for i in "${arr[#]}"; do
if ! [[ $2 == "$i" ]]; then
match=1
break
fi
done
if [[ $match = 1 ]]; then
echo "No match found"
fi
Any idea how to loop in array and popup error if value doesn't exist ?
There are already answers about your question see check value is in an array, but a fix/idea for your specific approach is something like.
#!/usr/bin/env bash
declare -a arr=("crm" "hr" "pos")
match=0
for i in "${arr[#]}"; do
if ! [[ $2 == "$i" ]]; then
((match++))
fi
done
if (( match == ${#arr[*]} )); then
printf >&2 "No match found\n"
fi
The above script increments match every time the test inside the for loop is true, and in the end compare match with the length of the array ${#arr[*]}. A more verbose output is to put set -x on after the shebang and add a $ sign to the variable match (which is not required) inside the (( )).
if (( $match == ${#arr[*]} )); then
Your original approach always breaks the loop when it does not have a match, doing so will not continue the loop.
You reversed the logic. Just leave out the ! and swap the final test:
match=0
for i in "${arr[#]}"; do
if [[ $2 == "$i" ]]; then
match=1
break
fi
done
if [[ $match = 0 ]]; then
echo "No match found"
fi
This doesn't require a loop.
c.f. if a 2 strings BOTH present in 1 array in a bash script
$: x=bar
$: [[ " ${arr[*]} " =~ " $x " ]] && echo found || echo nope
nope
$: x=hr
$: [[ " ${arr[*]} " =~ " $x " ]] && echo found || echo nope
found
or
$: match=0
$: set -- foo bar
$: [[ " ${arr[*]} " =~ " $2 " ]] && match=1
$: echo match
0
$: set -- foo crm
$: [[ " ${arr[*]} " =~ " $2 " ]] && match=1
$: echo match
1
The logic seems incorrect.
If it does not match, you break the loop.
If it does match, the loop goes to the next iteration. Then it becomes unmatched and finally breaks the loop
declare -a arr=("crm" "hr" "pos")
not_match=0
for i in "${arr[#]}"; do
if [ "$2" = "$i" ]; then
not_match=1
break
fi
done
if [ $not_match -eq 0 ]; then
echo "No match found"
fi

Appending to an array in bash, why this isn't working?

I'm trying from a list of files with a pattern correctly matched by regex to check whether this value is in my array, if not, append it.
Unfortunately, this code that I build up inspired by some stack overflow post doesn't work (nothing is happened, the =~ doesn't seem to find the bash_rematch, and also it doesn't output anything?
sample_array=() #creating the array
for context_files in data/*.txt.gz # checking all the different samples id we have
do
[[ $context_files =~ SL[0-9]{6} ]]
echo 'context file:' "$context_files"
echo 'rematch:' "${BASH_REMATCH[0]}"
if ! [[ " ${sample_array[*]} " =~ (^|[[:space:]])"${BASH_REMATCH[0]}"($|[[:space:]]) ]]; then
echo 'condition matched'
echo 'rematch:' "${BASH_REMATCH[0]}"
sample_array+=(" ${BASH_REMATCH[0]} ")
fi
done
echo "${sample_array[*]}"
replacing this code by
sample_array=() #creating the array
for context_files in data/*.txt.gz # checking all the different samples id we have
do
[[ $context_files =~ SL[0-9]{6} ]]
echo 'context file:' "$context_files"
echo 'rematch:' "${BASH_REMATCH[0]}"
if ! [[ " ${sample_array[*]} " == "${BASH_REMATCH[0]}" ]]; then
echo 'condition matched'
echo 'rematch:' "${BASH_REMATCH[0]}"
sample_array+=(" ${BASH_REMATCH[0]} ")
fi
done
echo "${sample_array[*]}"
will this time add all the variable
output :
A B A B A B
I probably don't get something in how the if is managed and/or how the regex lookup in a bash array is to be made but I'd gladly get some help!
The second match is negated, so in order to enter the then part, the match needs to fail. A failed match resets $BASH_REMATCH.
#! /bin/bash
sample_array=()
for context_files in data/SL{111111,222222,333333,111111,222222}.txt.gz ; do
[[ $context_files =~ SL[0-9]{6} ]]
match=${BASH_REMATCH[0]}
echo 'context file:' "$context_files"
echo 'rematch:' "$match"
if ! [[ " ${sample_array[*]} " =~ (^|[[:space:]])"$match"($|[[:space:]]) ]]; then
echo 'condition matched'
echo 'rematch:' "$match"
sample_array+=(" $match ")
fi
done
echo "${sample_array[*]}"
Here is a completely alternative solution in bash-style like John Kugelman suggested:
printf %s\\n data/*.txt.gz | grep -Eo 'SL[0-9]{6}' | sort -u
If you need the results in an array, use mapfile:
mapfile -t array <(printf %s\\n data/*.txt.gz | grep -Eo 'SL[0-9]{6}' | sort -u)

In Bash, how to convert number list into ranges of numbers?

Currently I have a sorted output of numbers from a command:
18,19,62,161,162,163,165
I would like to condense these number lists into a list of single numbers or ranges of numbers
18-19,62,161-163,165
I thought about trying to sort through the array in bash and read the next number to see if it is +1... I have a PHP function that does essentially the same thing, but I'm having trouble transposing it to Bash:
foreach ($missing as $key => $tag) {
$next = $missing[$key+1];
if (!isset($first)) {
$first = $tag;
}
if($next != $tag + 1) {
if($first == $tag) {
echo '<tr><td>'.$tag.'</td></tr>';
} else {
echo '<tr><td>'.$first.'-'.$tag.'</td></tr>';
}
unset($first);
}
}
I'm thinking there's probably a one-liner in bash that could do this but my Googling is coming up short....
UPDATE:
Thank you #Karoly Horvath for a quick answer which I used to finish my project. I'd sure be interested in any simpler solutions out there.
Yes, shell does variable substitution, if prev is not set, that line becomes:
if [ -ne $n+1]
Here is a working version:
numbers="18,19,62,161,162,163,165"
echo $numbers, | sed "s/,/\n/g" | while read num; do
if [[ -z $first ]]; then
first=$num; last=$num; continue;
fi
if [[ num -ne $((last + 1)) ]]; then
if [[ first -eq last ]]; then echo $first; else echo $first-$last; fi
first=$num; last=$num
else
: $((last++))
fi
done | paste -sd ","
18-19,62,161-163,165
Only with a function in bash:
#!/bin/bash
list2range() {
set -- ${#//,/ } # convert string to parameters
local first a b string IFS
local -a array
local endofrange=0
while [[ $# -ge 1 ]]; do
a=$1; shift; b=$1
if [[ $a+1 -eq $b ]]; then
if [[ $endofrange -eq 0 ]]; then
first=$a
endofrange=1
fi
else
if [[ $endofrange -eq 1 ]]; then
array+=($first-$a)
else
array+=($a)
fi
endofrange=0
fi
done
IFS=","; echo "${array[*]}"
}
list2range 18,19,62,161,162,163,165
Output:
18-19,62,161-163,165

Bash passing array to expr

I'm writing a simple calculator in BASH. Its aim is to loop through the arguments given, check if they're correct, do the power function and pass the rest to the expr to do the calculation. Everything except multiplication works.
When I do something like
my_script.sh 2 \* 2
I get syntax error from expr. Checking with bash -x lets me know that
expr 2 '\*' 2
The * is in apostrophes. I don't know how to get rid of it so the expr can parse it properly.
if [ $# -le 0 ]
then
usage
exit 1
fi
ARGS=("${#}")
while [ $# -gt 0 ]
do
if [ $OP -eq 0 ]
then
if [[ $1 =~ ^[-]{0,1}[0-9]+$ ]]
then
ELEMS[$J]=$1
shift
let OP=1
let J=$J+1
else
echo $1' is not a number'
usage
exit 3
fi
else
if [[ $1 =~ ^[-+/\^\*]{1}$ ]]
then
if [[ $1 =~ ^[\^]{1}$ ]]
then
if ! [[ $2 =~ ^[0-9]+$ ]]
then
echo 'Bad power exponent'
usage
exit 3
fi
let BASE=${ELEMS[$J-1]}
let EX=$2
pow $BASE $EX
let ELEMS[$J-1]=$RES
shift 2
else
if [[ $1 =~ [\*]{1} ]]
then
ELEMS[$J]=\\*
else
ELEMS[$J]=$1
fi
let J=$J+1
shift
let OP=0
fi
else
echo $1' is not an operator'
if [[ $1 =~ ^[0-9]+$ ]]
then
let TMP=${ELEMS[$J-1]}
echo "Are you missing an operator beetwen $TMP and $1?"
fi
usage
exit 3
fi
fi
done
if [ $OP -eq 0 ]
then
echo 'Missing argument after last operator'
usage
exit 3
fi
echo "Calculation: ${ARGS[*]}"
echo -n 'Result: '
expr ${ELEMS[*]}
Change ELEMS[$J]=\\* to ELEMS[$J]="*" (or ELEMS[$J]=\*) and use:
expr "${ELEMS[#]}"
The key is to use # instead of * in the array dereference, which allows you to use double quotes. This is equivalent to expr "2" "*" "2", instead of expr "2 * 2" which you get when using expr "${ELEMS[*]}"

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

Resources