Reputation: 23
Sometimes I need to find a specific serial in a box with many items, so I wrote a simple Bash script that allows me to use a barcode scanner to scan hundreds of barcodes until a match is found, at which point the screen flashes (so I can see it from the corner of my eyes while looking at the box).
The script works great, but it only checks against one specific serial number provided by the user. Here's the code:
#!/bin/bash
INPUT=''
SCAN=''
SN=''
I='0'
clear
printf "Enter serial\n"
read INPUT
SN=`printf "${INPUT}" | tr '[:lower:]' '[:upper:]'`
# Keep comparing scans to needed serial until a match is found
while [[ "${SCAN}" != *"${SN}"* ]];
do
clear
printf "Looking for [ "${SN}" ]\n"
printf "Please scan barcode\n"
read INPUT
SCAN=`printf "${INPUT}" | tr '[:lower:]' '[:upper:]'`
done
# Flash screen when match is found
while [[ "${I}" -lt 3 ]];
do
printf '\e[?5h' && sleep 0.3
printf '\e[?5l' && sleep 0.3
I=$[${I}+1]
done
printf "FOUND\n"
Today I spent hours trying to implement a way to pass multiple possible serial numbers as command line arguments, but I can't seem to get it working. I would like to be able to pass a small, manageable number of possible serials, like this:
$ ./script.sh sn1 sn2 sn3 sn4 sn5
And for the script continue asking for input until I come across the item I am looking for.
I've studied the handling of shell arguments, but I can't seem to "massage" the above while loop to get it to check if the scanned serial exists in the array (created from the command line arguments passed):
#!/bin/bash
snList=( "$@" )
INPUT=''
SCAN=''
SN=''
I='0'
clear
#displaying "things" so I can see what each variable contains (debugging)
printf "$@\n"
printf "$0\n"
printf "$*\n"
printf "$0\n"
printf "$1\n"
printf "$2\n"
printf "$3\n"
printf "snList: $snList\n"
printf "snList[@]: ${snList[@]}\n"
printf "snList[*]: ${snList[*]}\n"
# Keep comparing scans to needed serial until a match is found
while [[ ! " ${snList[*]} " =~ "${SCAN}" ]];
do
clear
printf "Looking for [ "$*" ]\n"
printf "Please scan barcode\n"
read INPUT
SCAN=`printf "${INPUT}" | tr '[:lower:]' '[:upper:]'`
done
I've tried using ${snList[@]} in the loop as well, same result, it behaves like a match was found immediately, without even asking for a scan (indicating that the content of the while loop is not being executed).
Any help will be immensely appreciated, I think I am close, but I can't figure out what I am doing wrong.
Thanks in advance!
Upvotes: 1
Views: 464
Reputation: 7831
Something like this maybe?
#!/usr/bin/env bash
to_compare_input=("$@")
exglob_pattern_input=$(IFS='|'; printf '%s' "@(${to_compare_input[*]})")
until [[ $user_input == $exglob_pattern_input ]]; do
read -r user_input
done
Run the script with the the following arguments.
bash -x ./myscript foo bar baz more
Output
+ to_compare_input=("$@")
++ IFS='|'
++ printf %s '@(foo|bar|baz|more)'
+ exglob_pattern_input='@(foo|bar|baz|more)'
+ [[ '' == @(foo|bar|baz|more) ]]
+ read -r user_input
papa
+ [[ papa == @(foo|bar|baz|more) ]]
+ read -r user_input
mama
+ [[ mama == @(foo|bar|baz|more) ]]
+ read -r user_input
baz
+ [[ baz == @(foo|bar|baz|more) ]]
The first user input is empty since the builtinread
has not been executed to ask for the user's input. As shown at the debug message.
+ [[ '' == @(foo|bar|baz|more) ]]
The second (assuming the user has entered papa) is papa
The third (assuming the user has entered mama) is mama
The last is baz
which breaks out of off the until
loop, because it belongs to the $extglob_pattern_input
, which is an extglob
feature.
A regex is also an alternative using the =~
operator.
#!/usr/bin/env bash
to_compare_input=("$@")
regex_pattern_input=$(IFS='|'; printf '%s' "^(${to_compare_input[*]})$")
until [[ $user_input =~ $regex_pattern_input ]]; do
read -r user_input
done
Run the script same as before.
Using two loops which was suggested in the comments section.
#!/usr/bin/env bash
to_compare_input=("$@")
inarray() {
local n=$1 h
shift
for h; do
[[ $n == "$h" ]] && return
done
return 1
}
until inarray "$user_input" "${to_compare_input[@]}"; do
read -r user_input
done
As for the tr
if your version of bash supports the ^^
and ,,
for uppercase and lowercase parameter expansion. use ${user_input^^}
until [[ ${user_input^^} == $exglob_pattern_input ]]; do
until [[ ${user_input^^} =~ $regex_pattern_input ]]; do
until inarray "${user_input^^}" "${to_compare_input[@]}"; do
Upvotes: 2
Reputation: 1839
Assuming no spaces in the bar code texts. You can do something like this
while read -r INPUT
do
#Append spaces to prevent substring matching
if [[ $(echo " $@ " | grep -i " ${INPUT} " | wc -l) -eq 1 ]]
then
break
fi
done
Upvotes: 1