I'm unfamiliar with bash scripting. I wrote a script check arguments. the code is:
for (( i=1; i<=4; i++ ))
do
if ! [[ "$"$i =~ .*[^0-9].* ]]; then
echo "bad input was $i"
fi
done
Actually i want to split non numerical arguments, But it seems that "$"$i is wrong because the answer is always true or false independent of arguments.
can anybody tell me what is the mistake?
You seem to be trying to use indirect parameter expansion.
for (( i=1; i<=4; i++ ))
do
if ! [[ ${!i} =~ .*[^0-9].* ]]; then
echo "bad input was $i"
fi
done
However, it's cleaner to just iterate over the parameters directly, rather than over their position:
for arg in "${#:1:4}"; do
if ! [[ $arg =~ .*[^0-9].* ]]; then
echo "bad input was $arg"
fi
done
If condition should be like this:
if [[ ! "$i" =~ [^0-9] ]]; then
OR remove 2 negatives:
if [[ "$i" =~ [0-9] ]]; then
OR use glob:
if [[ "$i" == *[0-9]* ]]; then
Which means $i contains a digit 0-9
Update: Based on your comments it looks like you are looking for BASH variable indirection like this script check-num.sh:
#!/bin/bash
for (( i=1; i<=$#; i++ )); do
[[ "${!i}" != *[0-9]* ]] && echo "bad input was ${!i}"
done
You can run this script as: ./check-num.sh 1 2 x 4 a
Note how ${!i} syntax is being used here to access the variable's $1, $2, $3 etc that is called BASH variable indirection. You shouldn't use $$i for this purpose.
As per BASH manual:
If the first character of parameter is an exclamation point, a level of variable indirection is introduced. Bash uses the value of the variable formed from
the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the
value of parameter itself.
Use something like this :
for i in "$#"; do
[[ $i =~ .*[^0-9].* ]] || echo "bad input was $i"
done
N.B : It's not necessary to use doubles quotes arround the variable with the [[ internal instruction.
Related
I am novice to linux scripting. For the below example, i need to split the string as per "-" and store the output in an array as a separate element.
Later, i need to validate each element in an array if its an integer or alphanumeric. if its integer, i need to ignore that element and print only non-integer elements. The following script which i am trying is not giving expected output which should be like 'grub2-systemd-sleep-plugin'.
item = grub2-systemd-sleep-plugin-2.02-153.1
IFS='-'
read -rasplitIFS<<< "$item"
for word in "${splitIFS[#]}"; do echo $word; done
Taking a stab at this here...
Depends on how your numbers may be defined, but I believe you could use something like this to removing numbers from the output. I'm not sure if there is a more efficient way to achieve this
for word in ${splitIFS[#]}
do
c=$(echo $word | grep -c -E "^[0-9]+\.{0,}[0-9]+$")
[ $c -eq 0 ] && echo $word
done
If you're using bash, it will be faster if you use built-in tools rather than subshells.
line=grub2-systemd-sleep-plugin-2.02-153.1-foo-1
while [[ -n "$line" ]]
do if [[ "$line" =~ ^[0-9.]+- ]]
then line="${line#*-}"
elif [[ "$line" =~ ^[0-9.]+$ ]]
then break
elif [[ "$line" =~ ^([[:alnum:]]+)- ]]
then echo "${BASH_REMATCH[1]}";
line="${line#*-}"
elif [[ "$line" =~ ^([[:alnum:]]+)$ ]]
then echo "${BASH_REMATCH[1]}";
break
else echo "How did I get here?"
fi
done
or if you prefer,
shopt -s extglob
line=grub2-systemd-sleep-plugin-2.02-153.1-foo-1
while [[ -n "$line" ]]
do case "$line" in
+([0-9.])-*) line="${line#*-}" ;;
+([0-9.])) break ;;
+([[:alnum:]])-*) echo "${line%%-*}"
line="${line#*-}" ;;
+([[:alnum:]])) echo "$line"
break ;;
*) echo "How did I get here?" ;;
esac
done
I need some help in my shell script. I have this:
#!/bin/bash
for i in $*
do
if [[ $i = *[a-zA-Z] ]]
then echo $i contains just letters.
elif [[ $i = *[a-zA-Z0-9] ]]
then echo $i contains numbers and letters.
else
echo Error.
done
I would like the result to be, for example:
$ ./script.sh abCd a9d a-b
abCd contains just letters.
a9d contains numbers and letters.
Error.
But I get contains just letters in every case.
I also tried grep command too, but without success.
Your RegEx are wrong. Try the following:
#!/bin/bash
for i in $*
do
if [[ $i =~ ^[a-zA-Z]+$ ]]
then echo $i contains just letters.
elif [[ $i =~ ^[a-zA-Z0-9]+$ ]]
then echo $i contains numbers and letters.
else
echo Error.
fi
done
I need to make a script that iterates through a list of parameters and checks/counts if the parameter starts with an uppercase letter. I have some starter code but I am stuck and would appreciate any help!
Several notes:
You're missing the =~ operator for a regular expression
Your if is not ended by a fi.
Using [A-Z] doesn't work in all locales, and is needlessly fragile. Some collation orders are of the form AaBbCcDd, and thus A-Z contains a, b, etc; [[:upper:]] is guaranteed to do the right thing everywhere.
Unquoted $# behaves exactly the same as unquoted $*. If you want to correctly honor the quoting and escaping used when your function was first called, use "$#", quoted.
Consider instead:
#!/bin/bash
(( "$#" )) || { echo "Error: No arguments given" >&2; exit 1; }
re='^[[:upper:]]' # store regex in a variable for compatibility with old bash releases
for word in "$#"; do
[[ $word =~ $re ]] && ((++count))
done
echo "$count arguments started with upper-case characters"
Alternately, by using a case statement you can avoid requiring bash, and also check for other types:
for word in "$#"; do
case $word in
[[:upper:]]*) (( ++upper_count )) ;;
[[:lower:]]*) (( ++lower_count )) ;;
[[:digit:]]*) (( ++digit_count )) ;;
esac
done
echo "Found $upper_count arguments starting with upper-case letters"
echo "Found $lower_count arguments starting with lower-case letters"
echo "Found $digit_count arguments starting with digits"
#! /bin/bash
if [ $# -eq 0 ]; then
echo Error
exit 1
fi
COUNT=`echo "$#" | tr ' ' '\n' | grep "^[A-Z]" | wc -l`
echo $COUNT
I'd like to use the result of a comparison in a subsequent comparison. I'm trying to do something like:
# $1 - expected result
# $2 - actual result
function print_result() {
if [[ [[ $1 -eq 0 ]] -eq [[ $2 -eq 0 ]] ]]; then # invalid
echo "Pass"
else
echo "Fail"
fi
}
I can get the desired behaviour with the more verbose form:
function print_result() {
if [[ (($1 -eq 0) && ($2 -eq 0)) || (($1 -ne 0) && ($2 -ne 0)) ]]; then
echo "Pass"
else
echo "Fail"
fi
}
but it seems like there should be a simpler way?
You need something which produces the result of the comparison operator as text rather than as a status return. That would be arithmetic expansion: $((expression)).
Note also that bash includes a numeric conditional compound statement -- (( expr )) -- which is often easier to use for numerical comparisons than the non-numerical [[ ... ]] conditional compound statement.
Putting that together, what you are looking for is:
if (( $(($1==0)) == $(($2==0)) )); then
Or just:
if (( ($1==0) == ($2==0) )); then
If you only want to know if $1 and $2 are both zero or both non-zero, then you can use the fact that boolean not (!) always evaluates to either 0 or 1. Consequently, the following even simpler expression is equivalent (but see warning below):
if ((!$1 == !$2)); then
Important: If you use that in a script, it will work fine, but at the command prompt you need to put a space after the ! to avoid it being interpreted as a history expansion character (unless you've turned history expansion off: if you don't actually use history expansion, turning it off is not a bad idea.)
A possibly slightly more readable (but not particularly simpler) way would be to store the result of each comparison in a variable and then compare the variables:
function print_result() {
[[ $1 -eq 0 ]]; arg_one_is_zero=$?
[[ $2 -eq 0 ]]; arg_two_is_zero=$?
if [[ $arg_one_is_zero -eq $arg_two_is_zero ]]; then
echo "Pass"
else
echo "Fail"
fi
}
There might be a better way to store the logical result (exit code) of a comparison in a variable but I couldn't find one after a quick look.
When given a string I want to search for a substring which matches two characters (9&0. 0 should be the last character in that substring) and exactly two characters in between them
string="asd20 92x0x 72x0 YX92s0 0xx0 92x0x"
#I want to select substring YX92s0 from that above string
for var in $string
do
if [[ "$var" == *9**0 ]]; then
echo $var // Should print YX92s0 only
fi
done
Obviously this above command doesn't work.
You match each element against the pattern *9??0. There are several ways you can do this; here's one that uses the string to set the positional parameters in a subshell, then iterates over them in a for loop:
( set -- $string
for elt; do [[ $elt == *9??0 ]] && { echo "found"; exit; }; done )
string="asd20 92x0x 72x0 X92s0 0xx0"
if [[ $string =~ [[:space:]].?9.{2}0[[:space:]] ]]; then
echo "found"
fi
Or better, taking advantage of word spliting :
string="asd20 92x0x 72x0 X92s0 0xx0"
for s in $string; do
if [[ $s =~ (.*9.{2}0) ]]; then
echo "${BASH_REMATCH[1]} found"
fi
done
This is regex with bash.