3

I am working on a script and I have a while in which I want to take the user input and use as an integer value with a counter like so:

read -p "How many bytes would you like you replace :> " $numOfBytes
echo "$numOfBytes bytes to replace"
while [ $counter -le $numOfBytes ]
do
    echo "testing counter value = $counter"
    let $counter++
done

To my understanding it doesn't currently work because it is taking the numOfBytes variable as a string.

Do I need to convert the string to an int some how? Is it possible to do something like that? Is there an alternative?

1
  • Drop the $ on $numOfBytes in the read command, and use counter=$(( counter + 1 )) for the increment. Also double quote you variable expansions.
    – Kusalananda
    May 15, 2018 at 17:14

3 Answers 3

6

You want to read an integer and then do a loop from 1 to that integer, printing the number in each iteration:

#!/bin/bash

read -p 'number please: ' num

for (( i = 1; i <= num; ++i )); do
    printf 'counter is at %d\n' "$i"
done 

Notice how the $ is not used when reading the value. With $var you get the value of the variable var, but read needs to know the name of the variable to read into, not its value.

or, with a while loop,

#!/bin/bash

read -p 'number please: ' num

i=0
while (( ++i <= num )); do
    printf 'counter is at %d\n' "$i"
done

The (( ... )) in bash is an arithmetic context. In such a context, you don't need to put $ on your variables, and variables' values will be interpreted as integers.

or, with /bin/sh,

#!/bin/sh

printf 'number please: ' >&2
read num

i=1
while [ "$i" -le "$num" ]; do
    printf 'counter is at %d\n' "$i"
    i=$(( i + 1 ))
done

The -le ("less than or equal") test needs to act on two quoted variable expansions (in this code). If they were unquoted, as in [ $i -le $num ], then, if either variable contained a shell globbing character or a space, you might get unexpected results, or errors. Also, quoting protects the numbers in case the IFS variable happens to contain digits.

Related questions:

0
3

To address the specific question:

Do I need to convert the string to an int some how?

The answer is no. Shell variables are all strings, but depending on the context in which they're used they can be treated as integers or strings. In case of -le operator for [ command (aka test command), variables will be treated as integers.

# integer comparison
$ var=25; test "$var" -le "$HOME"
bash: test: /home/username: integer expression expected
$ test "$var" -le 30 && echo Lower
Lower
# string comparison
$ test $var = 24 && echo 'same string' || echo 'different string'
different string
$ test $var = 25 && echo 'same string' || echo 'different string'
same string

Your script needs to initialize counter variable, drop $ from numOfBytes in read , and remove $ from let.

#!/bin/bash

read -p "How many bytes would you like you replace :> " numOfBytes
echo "$numOfBytes bytes to replace"
counter=0
while [ "$counter" -le "$numOfBytes" ]
do
    echo "testing counter value = $counter"
    let counter++
done

This works as so:

$ ./counter.sh 
How many bytes would you like you replace :> 5
5 bytes to replace
testing counter value = 0
testing counter value = 1
testing counter value = 2
testing counter value = 3
testing counter value = 4
testing counter value = 5

Note that due to let being bash/ksh keyword, this makes the script less portable. It'd be advisable to make use of arithmetic expansion counter=$((counter+1)), which is part of POSIX's Shell Language standard (section 2.6.4).

See also: https://askubuntu.com/a/939299/295286


As Storm Dragon pointed out, the fact that shell variables are treated depending on their context also implies that user's input needs to be sanitized. One possible way is to take numOfBytes into portable case steatement as in this answer, which takes care of determining whether the input is actually a digit.

3

Bash doesn't really have assignable types. The variable is the type of its declaration. So, if you declare a variable as abc, it's a string. If you declare it as 138, it's an int. Bash doesn't really handle decimals, so if you declare variable as 138.0, it treats it like a string.

So, you need to be careful with user input, because you can't loop from 0 to abc. Here is an example that should give some error checking and increment through the bytes in a loop.

#!/bin/bash

# Handle non-numeric input
read -p "How many bytes would you like you replace :> " numOfBytes
while [[ ! "$numOfBytes" =~ ^[0-9]+$ ]]; do
    echo
    echo "Please enter whole numbers only:"
    read -p "How many bytes would you like you replace :> " numOfBytes
done

echo "$numOfBytes bytes to replace $numOfBytes"
for i  in $(seq $numOfBytes) ; do
    echo "i has been incremented to $i."
done
exit 0
1
  • C-style for loops should be used in Bash instead of seq. May 15, 2018 at 23:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .