Bash: How to check in array check if input was entered twice? - bash

The array variable ARRAY_ITEMS adding input from the user.
Here my script.
#!/bin/bash
var1=$(echo -e "Adding $INPUT to array.")
while true; do
printf "\n
A B C\n"
echo -e "This is the array: $ARRAY_ITEMS: "
read -p "Input: " INPUT
case "$INPUT" in
A) echo $var1 ; ARRAY_ITEMS+=$INPUT ;;
B) echo $var1 ; ARRAY_ITEMS+=$INPUT ;;
C) echo $var1 ; ARRAY_ITEMS+=$INPUT ;;
*) echo -e "Whoopsy! Invalid input." ;;
esac
done
The Output looks like this:
A B C This is the array: ABCAAABCAAA:
Input: D Whoopsy! Invalid input.
How does my script have to look like so that the following output appears for an input that has already been entered?
A B C
This is the array: ABC:
Input: A
ERROR. A is already selected.

Use pattern-matching to see if the input is already in the string.
items=
while true; do
printf "\n
A B C\n"
echo "This is the array: $items"
read -p "Input: " input
case "$INPUT" in
A|B|C) if [[ $items = *$input* ]]; then
echo "Error, $input is already in $items"
else
items+=$input
fi ;;
*) echo "Whoopsy! Invalid input." ;;
esac
done
If the input could be something more than a single letter, you'll need to use an array and resign yourself to walking the array, one element at a time, to compare them to the input. Something like
items=()
while true; do
...
found=0
case $input in
A|B|C) for x in "${items[#]}"; do
if [[ $x == $input ]]; then
echo "Error, already used $input"
found=1
break
fi
done
;;
*) echo "Error, invalid input" ;;
esac
(( found )) || items+=("$input")
done

Related

Shell variable value passing to subshell?

I am writing script in shell and it's something like:
temp=0
while true; do
case "a" in
a) temp=5; ;;
esac
break
done | echo "$temp"
( temp value is condition for if in subshell and | (pipe) is needed as stdout redirect to stdin)
and I need in temp 5 but I got 0 in echo. Any way to preserve value inside case (posixly correct without tempfile)?
Do you want it like this? Prints 5 and 0.
#!/bin/sh
temp=0
while true; do
case "a" in
a) temp=5; echo "$temp" ;;
esac
break
done | cat
echo "temp unchanged because of subshell $temp"
Would this help you, filtering out your status?
I still don't get why you need a pipe and how you expect the other command you pipe into to react on a status. It's just not the way it works. You evaluate conditions in your script and call commands accordingly.
#!/bin/sh
filter(){
data=''
while read line; do
case "$line" in
temp=5) temp=5 ;;
*) data=$(printf '%s\n' "$data"; printf '%s\n' "$line") ;;
esac
done
if [ "$temp" -eq 5 ]; then
echo "do this 5 with data"
else
echo "do that other with data"
echo "$data"
fi
}
temp=0
while true; do
case "a" in
a) temp=5; printf 'temp=%s\n' "$temp"; for i in $(seq 1 30); do echo "30 lines"; done ;;
esac
break
done | filter
echo "temp unchanged because of subshell $temp"

Error getting an output from a users selection bash shell in Linux

He friends. I am creating a script file to be able to read users input and display an output based on the question i am asking. When i run the choices separately they work fine but cant figure out why they will not display when i put pieces together.
#!/bin/bash
while test $loop = "y"
do
echo "$CLEAR"
clear
tput cup 5 12 ; echo "L- List all wines by name in alphabetical order."
tput cup 6 12 ; echo "C- Count all wines in each type and list all 5 types with their count."
tput cup 10 12; echo "E- Exit the program."
read choice || continue
case $choice in
[Ll]) file="/home/students/domie/wine.txt"; sort -t ":" -k2 $file;; #no output here
[Cc]) ./count ;; #look below for count script
[Ee]) clear; exit ;; #this works when script runs
*) tput cup 14 4; echo "Invalid Code,Press Enter and Try Again"; read choice ;;
esac
done
and the count script:
$vi count
while IFS=":" read -ra line; do
if (( ${line[2]} == 2 )); then
IFS=":" && echo "${line[*]}"
(( count++ ))
fi
done < /home/students/domie/wine.txt
echo "Count = $count"
EDITED:
wine.txt
Try this:
#!/bin /bash
file="wine.txt"
count() {
# Replace below with your counting logic ..
while IFS=":" read -ra line; do
if (( ${line[2]} == 2 )); then
IFS=":" && echo "${line[*]}"
(( count++ ))
fi
done < "$file"
echo "Count = $count"
}
pause() {
read -p "$*"
}
while :; do
clear
echo "L- List all wines by name in alphabetical order."
echo "C- Count all wines in each type and list all 5 types with their count."
echo "E- Exit the program."
read -p "Enter Choice: " choice || continue
case $choice in
[Ll]) sort -t ":" -k2,2 "$file" ; pause 'Press [Enter] key to continue...';;
[Cc]) count ; pause 'Press [Enter] key to continue...';;
[Ee]) exit ;;
*) pause "Invalid Code, Press Enter and Try Again" ;;
esac
done

email shell script part 2

I'm creating a shell script that takes in user input and text's people using the mail function. I am looking to make it more advanced. Right now it just text's one person at a time, I want it to have the ability to text multiple people or even everyone with a user input of 'All'.
#!/bin/sh
# Prefix the numbers with something
number_Joe=8881235555
number_Bob=8881235556
echo "Who do you want to text?:(i.e. Joe, Bob, etc)"
read name
echo "What do you want to say?:"
read quote
# Remove any dangerous characters that the user enters
sanitized=$(printf "%s" "$name" | tr -cd 'a-zA-Z')
#Look up by evaluating e.g. "number=$number_Joe"
eval "number=\$number_$sanitized"
if [ "$number" ]
then
echo "texting $name ($number) with $quote"
printf "%s\n" "$quote" | mailx -s "Text Message via email" "$number#txt.att.net"
else
echo "Unknown user"
exit 1
fi
Also, is there a cleaner method of bringing in a external txt file that houses the numbers instead of the script?
(note: we still have bash <4, thus why I'm not using a associative array)
Here's a rewrite.
Should work fine in bash3.
#!/bin/bash
# Prefix the numbers with something
names=()
names+=(Joe); numberJoe=8881235555
names+=(Bob); numberBob=8881235556
domain=txt.att.example.com
usage () {
echo "usage: $(basename $0) names message ..."
echo "where: names is a comma-separated list of names (no spaces)"
echo
echo "example: $(basename $0) Jim,Fred hello lads, this is my message"
}
while getopts ":hl" opt; do
case $opt in
h) usage; exit ;;
l) IFS=,; echo "known names: ${names[#]}"; exit ;;
esac
done
shift $((OPTIND - 1))
if (( $# < 2 )); then
usage
exit
fi
IFS=, read -ra usernamelist <<<"$1"
shift
message="$*"
# validate names
namelist=()
for name in "${usernamelist[#]}"; do
if [[ " ${names[#]} " == *" $name "* ]]; then
namelist+=("$name")
else
echo "unknown name: $name" >&2
fi
done
if (( ${#namelist[#]} == 0 )); then
echo "no valid names given" >&2
exit 1
fi
# generate the recipient list
echo "texting '$message' to:"
recipients=()
for name in "${namelist[#]}"; do
numvar="number$name"
echo " $name -> ${!numvar}"
recipients+=( "${!numvar}#$domain" )
done
# send it
printf "%s\n" "$message" | mailx -s "Text Message via email" "$(IFS=,; echo "${recipients[*]}")"

How can I loop If Statements in Bash

Here is my code:
#!/bin/bash
echo "Letter:"
read a
if [ $a = "a" ]
then
echo "LOL"
fi
if [ $a = "b" ]
then
echo "ROFL"
fi
Is there a way for me to loop this so that, after displaying either LOL or ROFL, I would be asked for a letter again?
Yes.
Oh, you want to know how?
while true; do
echo "Letter:"
read a
if [ $a = "a" ]
then
echo "LOL"
elif [ $a = "b" ]
then
echo "ROFL"
fi
done
Of course, you probably want some way to get out of that infinite loop. The command to run in that case is break. I would write the whole thing like this:
while read -p Letter: a; do
case "$a" in
a) echo LOL;;
b) echo ROFL;;
q) break;;
esac
done
which lets you exit the loop either by entering 'q' or generating end-of-file (control-D).
Don't forget that you always want -r flag with read.
Also there is a quoting error on that line:
if [ $a = "a" ] # this will fail if a='*'
So here is a bit better version(I've also limited the user to input only 1 character):
#!/bin/bash
while true; do
read -rn1 -p 'Letter: ' a
echo
if [[ $a = 'a' ]]; then
echo "LOL"
elif [[ $a = 'b' ]]; then
echo "ROFL"
else
break
fi
done
Or with switch statement:
#!/bin/bash
while read -rn1 -p 'Letter: ' a; do
echo
case $a in
a) echo LOL;;
b) echo ROFL;;
*) break;;
esac
done

Getopts in bash not selecting option

I have one problem , when i select one option , for exemple ./test.sh -f it should print "mel" but it reads all code.
How does it enter the if condition and passes with other argument ?
if getopts :f:d:c:v: arg ; then
if [[ "${arg}" == d ]] ; then
d_ID=$OPTARG
eval d_SIZE=\$$OPTIND
else
echo "Option -d argument missing: needs 2 args"
echo "Please enter two args: <arg1> <arg2>"
read d_ID d_SIZE
echo "disc $d_ID $d_SIZE" >> $FILENAME
fi
if [[ "${arg}" == c ]] ; then
c_NOME="$OPTARG"
eval c_ID1=\$$OPTIND
eval c_ID2=\$$OPTINDplus1
eval c_FICHEIRO=\$$OPTINDplus2
else
echo "Option -c argument missing: needs 4 args"
echo "Please enter two args: <arg1> <arg2> <arg3> <agr4>"
read c_NOME c_ID1 c_ID2 c_FICHEIRO
echo "raidvss $c_NOME $c_ID1 $c_ID2 $c_FICHEIRO" >> $FILENAME
fi
if [[ "${arg}" == f ]] ; then
echo "mel"
fi
fi
You are using getopts parameters wrong.
if getopts :f:d:c:v: arg
means that -f will follow the value of parameter, like
-f 5
If you want just have -f (without value) you need to change it to
if getopts :fd:c:v: arg ; then
(I deleted the ':'). Also, I think you should better use while cycle and case statements.
See this example
while getopts fd:c:v: opt
do
case "$opt" in
f) echo "mel";;
d) discFunction "$OPTARG";;
c) otherFunction "$OPTARG";;
v) nop;;
\?) echo "$USAGE" >&2; exit 2;;
esac
done
shift `expr $OPTIND - 1`

Resources