Check if every argument is an integer in shell - shell

I've been killing myself over this trying to figure it out, and I know it's probably super simple, so hoping a new pair of eyes can help. I have a Bourne shell (sh) script that I'm writing and it takes a list of integers as the input (and amount from 1 integer up, ideally I'd like to take both positive and negative ints). I'm trying to do an error check for the case if someone inputs something other than an integer. They could input "1 2 3 4 5 a" and it'd give an error, because the a is not an int.
I have an error check for no inputs that works, and I have code that does stuff to the list of integers themselves, but even when strings are given it still gets to my final code block.
I currently have a for loop to iterate through each item in the list of integers, then an if loop to give the error message if the argument in question isn't an int. I've tried a few different versions of this, but this is the most recent one so I've put it below.
for i in $#; do
if [ $i -ge 0 ] 2>/dev/null; then
echo "Invalid input: integers only."
exit 1
fi
done

#!/bin/sh
#
for i in "$#"
do
case "${i#[-+]}" in
0)
echo cannot be 0?
exit 1
;;
*[!0-9]* | '')
echo not int
exit 2
;;
esac
done
echo i\'m ok
This should work, for both positive and negative ints. And if you admit that 0 is an integer, just delete the first case.
Almost duplicate: BASH: Test whether string is valid as an integer?
And here is a good answer for posix. https://stackoverflow.com/a/18620446/7714132

You could use a regex:
my_script.sh
for i in $# ; do
if ! [[ "$i" =~ ^-?[0-9]+$ ]] ; then
echo "Invalid input: integers only."
exit 1
fi
done
Example:
$ sh my_script.sh 1 2 3 4
$ sh my_script.sh 1 2 -12
$ sh my_script.sh 1 2 -12-2
Invalid input: integers only.
$ sh my_script.sh 1 2 a b
Invalid input: integers only.
Explanation of the regex:
^: beginning of the string
-?: 0 or 1 times the character -
[0-9]+: 1 or more digit
$: end of the string

In POSIX sh, you can match your string against a glob with case:
#!/bin/sh
for i
do
case "$i" in
*[!0-9]*)
echo "Integers only" >&2
exit 1
esac
done
echo "Ok"
Here's how it runs:
$ ./myscript 1 2 3 4 5
Ok
$ ./myscript 1 2 3 4 5 a
Integers only
The problem with your approach is primarily that you're checking for success rather than failure: [ will fail when the input is invalid.

Related

Using a positional parameter as the name of the array I want to loop over

I have been looking at other similar questions but didn't find the proper answer. $1 is the name of the array i want to loop over.
#!/bin/bash
for i in ${"$1"[#]}
do
echo "$i"
done
If I don't misunderstand your not very well written question, you are looking for bash indirection, which is available since bash, version 2.
Below a minimal example
#! /bin/bash
foo=( 1 2 3 4 )
bar=( 5 6 7 8 )
if [[ ( "$1" -ne foo && "$1" -ne bar ) ]];
then
echo "Usage: $0 <foo|bar>"
exit 1
fi
ambito="$1"[#] # here what you are looking for
for i in "${!ambito}"; # here the indirection
do
echo -n "$i "
done
echo ""
And you can call this indirection.sh scrit as:
$ ./indirection.sh foo
1 2 3 4
or
$ ./indirection.sh bar
5 6 7 8
This being said, using indirection may cause confusion. In most cases it could be replaced by associative arrays.
$1 is the first command line argument, not all of them. Probably what you are looking for is $#.
And to create an array holding them, something like:
ambito=($#)

how do you make a reverse number pyramid in bash

I'm trying to make a reverse pyramid that outputs
$ ./reverse_pyramid **3**
1 2 3 2 1
1 2 1
1
This is what I have but it outputs nothing and has no syntax errors:
#!/bin/bash
# get input
read -p "Enter number:" num
#outside of pyramid
for((i=1;i>=num;i--))
do
#Loop to print numbers
for((s=i;s>=num;s--))
do
echo -ne "#"
done
#left half
for((j=1;j<=i;j++))
do
echo -ne "$j"
done
#right
for((l=(i-1);l>=1;l--))
do
echo -ne "$l"
done
#add a line
echo
done
It's due to this line:
for((i=1;i>=num;i--))
you're setting i to 1 which is smaller than your input num of 3 so your loop won't execute.
As a side note, I recommend you use -x option when debugging bash programs:
bash -x myprogram.sh
This will show you all the execution steps and pinpoints where it might be going wrong

Script saying two commands not found when they've been entered

I'm writing a bash script as the first part of an assignment. If the number of arguments is two, it's supposed to return the sum; if it's anything but two, it's supposed to return the error message and exit the script.
But even when I enter two commands, it still gives me the error message. Why is that? I wrote something very similar -- subtracting numbers -- a second ago and it worked fine.
#!/bin/bash
# This script reads two integers a, b and
# calculates the sum of them
# script name: add.sh
read -p "Enter two values:" a b
if [ $# -ne 2 ]; then
echo "Pass me two arguments!"
else
echo "$a+$b=$(($a+$b))"
fi
read reads from standard input, while the arguments ($1, $2, ...) whose count you are checking with $# are command line arguments that can be passed to your program when it is called.
I'd suggest
read -p "Enter two values: " a b additional_garbage
if [[ -z $b ]]; then # only have to test $b to ensure we have 2 values
The "additional_garbage" is to guard against the funny user who enters more than 2 values, and then $b is something like "2 3 4" and your arithmetic is broken.
And to guard against invalid octal numbers (for example if the user enters 08 and 09), enforce base-10
echo "$a+$b=$(( 10#$a + 10#$b ))"

For loop in shell script with integer as input

I am trying to take integer as a input and trying to print numbers from 1 to the input number.
How can I do that?
Something like this will work:
read -p "Loop until: " n
for i in $(seq 1 $n); do
echo $i;
done
$n will contain the user input.
The seq program simply builds a sequence of numbers from 1 to $n, and the for loop prints every item in this sequence.
Instead of using seq command you can use the arithmetic initialisation in a shell.
You can use the following code to do it.
$ cat test
#!/bin/bash
read -p "Loop until: " n
a=1
while true; do
if [ $a -le $n ]; then
echo $a
else
break
fi
a=$(($a+1))
done
$ sh test
Loop until: 4
1
2
3
4

How do I find the number of arguments passed to a Bash script?

How do I find the number of arguments passed to a Bash script?
This is what I have currently:
#!/bin/bash
i=0
for var in "$#"
do
i=i+1
done
Are there other (better) ways of doing this?
The number of arguments is $#
Search for it on this page to learn more:
http://tldp.org/LDP/abs/html/internalvariables.html#ARGLIST
#!/bin/bash
echo "The number of arguments is: $#"
a=${#}
echo "The total length of all arguments is: ${#a}: "
count=0
for var in "$#"
do
echo "The length of argument '$var' is: ${#var}"
(( count++ ))
(( accum += ${#var} ))
done
echo "The counted number of arguments is: $count"
echo "The accumulated length of all arguments is: $accum"
to add the original reference:
You can get the number of arguments from the special parameter $#. Value of 0 means "no arguments". $# is read-only.
When used in conjunction with shift for argument processing, the special parameter $# is decremented each time Bash Builtin shift is executed.
see Bash Reference Manual in section 3.4.2 Special Parameters:
"The shell treats several parameters specially. These parameters may only be referenced"
and in this section for keyword $# "Expands to the number of positional parameters in decimal."
Below is the easy one -
cat countvariable.sh
echo "$#" | awk '{print NF}'
Output :
#./countvariable.sh 1 2 3 4 5 6
6
#./countvariable.sh 1 2 3 4 5 6 apple orange
8

Resources