bash shell script, issues with a 32 bit decimal to binary converter - bash

Having an issue with this
when I run it, the console repeats this over and over;
14: shellscript.sh: 2: not found
15: shellscript.sh: 32: not found
18: shellscript.sh: =1: not found
19: shellscript.sh: 0: not found
It seems like it's something to do with how bash handles redefining variables through arithmetic?
#!/bin/bash
echo "This script converts a user's number into an IP address."
echo -n "Input your number: "
read user
if [ $user -lt 4294967296 ]
then
exp=$((32))
num=$((0))
ipb=""
while [ $exp -gt 0 ]
do
bit=expr 2 ** $exp
exp=expr $exp - 1
if [ $bit+$num -le $user ]
then
$ipb="${ipb}1"
num=expr $num + $bit
else
$ipb="${ipb}0"
fi
done
echo $ipb
echo "done"
fi
Same as above but with comments to explain it.
#!/bin/bash
echo "This script converts a user's number into an IP address."
echo -n "Input your number: "
read user
#check if number is larger than 32bits
if [ $user < 4294967296 ]
then
#var exp is exponent that will be used to redefine var bit each loop cycle
#var num is var used to rebuild the user number with corresponding bits added to -
#var ipb is IP binary (not yet converted to 4 octet integers)
exp=$((32))
num=$((0))
ipb=""
#while the exponent is greater than 0 (exponent is 1 less as per binary order)
while [ $exp > 0 ]
do
#(Re)define bit var for line 23
bit=expr 2**$exp
#go to next lowest exponent
exp=expr $exp - 1
#If the current bit is added to our num var,will it be
#less than or equal to the user number?
if [ $bit + $num -le $user ]
then
#If so, redefine the ipb string var with a 1 on the end
#and redefine the num integer var added with the current
#iteration of the bit integer var's value
$ipb="${ipb}1"
num=expr $num + $bit
else
#if not, redefine the ipb string var with a 0 on the end
$ipb="${ipb}0"
fi
done
#output the IP binary
echo $ipb
echo "done"
fi
EDIT:
After some googling and help from shellcheck I got it to work. for some reason with my version of linux mint, the let command was the only thing that correctly took 2**31 as an exponent operation. Here's the code for anyone curious.
echo "This script converts a user's number into the 32 bit equivalent."
echo -n "Input a number below 4294967296: "
read user
echo ""
if [ $user -lt 4294967296 ]
then
exp=$((31))
num=$((0))
ipb=""
while [ $exp -ge 0 ]
do
let bit=2**$exp
let exp-=1
if (( $bit + $num <= $user ))
then
ipb="${ipb}1"
num=$(($num + $bit))
else
ipb="${ipb}0"
fi
done
fi
echo $ipb
Be sure to use bash instead of sh or ./ when running the script in terminal.

Your script has several issues. This one does not throw any errors.
#! /bin/bash
echo "This script converts a user's number into an IP address."
echo -n "Input your number: "
read user
if [ $user -lt 4294967296 ]
then
exp=$((32))
num=$((0))
ipb=""
while [ $exp -gt 0 ]
do
bit=$((2 ** $exp))
exp=$(expr $exp - 1)
if (( bit+num < user ))
then
ipb="${ipb}1"
num=$(expr $num + $bit)
else
ipb="${ipb}0"
fi
done
echo $ipb
echo "done"
fi
One major issue is that expr cannot calculate powers. You also do not use command substitution when you are trying to assign the result of expr to a variable.
Another major issue is that you cannot do math inside single [ ] brackets as you try to do in an if branch. Use (( )) instead.
There were a few other issue, e.g., $ipb="${ipb}1" expanding the variable that you actually want to assign.

Related

How to use a for loop to create folders in bash

I want to create a directory in which there is a bunch of text files using a for loop. Here is my code:
#!/bin/bash
echo "enter the nums: "
read num1 num2
for (( counter=0; counter<$num2; counter++ ))
do
if [ $num1 -lt 10 ] && [ $num2 -lt 10 ];
then
mkdir $num1 && touch $num1/$num1$num2.txt
echo "$num1""$num2" > $num1/$num1$num2.txt
else
echo "you weren't supposed to do that"
fi
done
What I want to happen if for example the user entered: "2 9"
Make a directory called 2
In it make text files called 290.txt, 291.txt, 292.txt... up till 299.txt.
Instead, what happens right now is it makes the directory and gives an error that the directory already exists. I don't know the next step, please help.
The biggest problem here is that you're doing things inside the loop that really only should be done once. Specifically, the error you're getting is because it tries to create the directory every time through the loop, but you can only create it once. Also, if the user enters too large a number, it'll print multiple error messages (e.g. if num2 is entered as 500, it'll print 500 error messages). You need to do both the error check and creating the directory once, before the loop.
A second problem is that you don't add $counter to the filename, so if the user enters "2 9", it'll create a file named 29.txt nine times.
You also have some more minor issues: in general, error messages should be printed to standard error instead of standard output (you can redirect them with >&2), and if there's an error the script should exit with a nonzero status. Also, you should (almost always) put double-quotes around variable references, to avoid weird results if the variables are blank or contain whitespace or some other things. You also don't need to touch files before writing into them (using > somefile will create the file if it doesn't exist).
With these things fixed (and some stylistic tweaks), here's what I get:
#!/bin/bash
echo "enter the nums: "
read num1 num2
if ! [ "$num1" -lt 10 ] || ! [ "$num2" -lt 10 ]; then
echo "you weren't supposed to do that" >&2 # message send to stderr
exit 1 # exit with error status
fi
mkdir "$num1" || exit $? # if mkdir fails, exit with its error status
for (( counter=0; counter<$num2; counter++ )); do
echo "${num1}${num2}" > "${num1}/${num1}${num2}${counter}.txt"
done
BTW, the ! [ "$num1" -lt 10 ] tests may look a little weird; why not just use [ "$num" -ge 10 ]? I did it that way in case $num1 and/or $num2 isn't a valid number, in which case both -lt and -ge tests would fail; using a negated test makes that an error rather than a success.
I'm not fluent in bash or anything, but it looks like mkdir $num1 is called on every loop. Find out first if the directory exists.
Here you are! change the if and for statement parent and child:
#!/bin/bash
echo "enter the nums: "
read num1 num2
if [ $num1 -lt 10 ] && [ $num2 -lt 10 ]; then
mkdir $num1
for i in $(seq 0 $num2); do
touch $num1/$num1$num2$i.txt
echo "$num1""$num2""$i" > $num1/$num1$num2$i.txt
done
else
echo "you weren't supposed to do that"
fi

Unix Scripting - Finding Minimum and Maximum (Bash Shell)

My code below is part of an assignment, but I'm racking my head against the desk not understanding why it won't assign a "MIN" value. I tried assigning the MIN and MAX to ${LIST[0]} just to have the first index in place, but it returns the whole array, which doesn't make sense to me. I'm executing this on a CentOS VM (which I can't see making a difference). I know the beginning of the first and second "if" statements need better logic, but I'm more concerned on the MIN and MAX outputs.
#!/bin/bash
LIST=()
read -p "Enter a set of numbers. " LIST
MIN=
MAX=
if [ ${#LIST[*]} == 0 ]; then echo "More numbers are needed."; fi
if [ ${#LIST[#]} -gt 0 ]; then
for i in ${LIST[#]}; do
if [[ $i -gt $MAX ]]; then
MAX=$i
fi
if [[ $i -lt $MIN ]]; then
MIN=$i
fi
done
echo Max is: $MAX.
echo Min is: $MIN.
fi
The code is almost functional.
Since $LIST is an array, not a variable, change read -p "Enter a set of numbers. " LIST to:
read -p "Enter a set of numbers. " -a LIST
Move the $MIN and $MAX init code down 5 lines, (just before the for loop):
MIN=
MAX=
...and change it to:
MIN=${LIST[0]}
MAX=$MIN
And it'll work. Test:
echo 3 5 6 | ./minmax.sh
Output:
Max is: 6.
Min is: 3.

bash - how to put $RANDOM into value?

newbie to bash:
basically I want to compare the result of $RANDOM to another value which is given by the user through 'read'
code for more info:
echo $RANDOM % 10 + 1 | bc
basically I want an if statement as well to see if the result of that $RANDOM value is equal to something that the user typed in e.g.:
if [ [$RANDOM VALUE] is same as $readinput
#readinput is the thing that was typed before
then
echo "well done you guessed it"
fi
something along the lines of that!!
to summarise
how do i make it so that i can compare a read input value to echo "$RANDOM % 10 + 1 | bc"
think of the program I am making as 'GUESS THE NUMBER!'
all help VERY MUCH APPRECIATED :)
There's no need for bc here -- since you're dealing in integers, native math will do.
printf 'Guess a number: '; read readinput
target=$(( (RANDOM % 10) + 1 )) ## or, less efficiently, target=$(bc <<<"$RANDOM % 10 + 1")
if [ "$readinput" = "$target" ]; then
echo "You correctly guessed $target"
else
echo "Sorry -- you guessed $readinput, but the real value is $target"
fi
The important thing, though, is the test command -- also named [.
test "$readinput" = "$target"
...is exactly the same as...
[ "$readinput" = "$target" ]
...which does the work of comparing two values and exiting with an exit status of 0 (which if will treat as true) should they match, or a nonzero exit status (which if will treat as false) otherwise.
The short answer is to use command substitution to store your randomly generated value, then ask the user for a guess, then compare the two. Here's a very simple example:
#/bin/bash
#Store the random number for comparison later using command substitution IE: $(command) or `command`
random=$(echo "$RANDOM % 10 + 1" | bc)
#Ask the user for their guess and store in variable user_guess
read -r -p "Enter your guess: " user_guess
#Compare the two numbers
if [ "$random" -eq "$user_guess" ]; then
echo "well done you guessed it"
else
echo "sorry, try again"
fi
Perhaps a more robust guessing program would be embedded in a loop so that it would keep asking the user until they got the correct answer. Also you should probably check that the user entered a whole number.
#!/bin/bash
keep_guessing=1
while [ "$keep_guessing" -eq 1 ]; do
#Ask the user for their guess and check that it is a whole number, if not start the loop over.
read -r -p "Enter your guess: " user_guess
[[ ! $user_guess =~ ^[0-9]+$ ]] && { echo "Please enter a number"; continue; }
#Store the random number for comparison later
random=$(echo "$RANDOM % 10 + 1" | bc)
#Compare the two numbers
if [ "$random" -eq "$user_guess" ]; then
echo "well done you guessed it"
keep_guessing=0
else
echo "sorry, try again"
fi
done

Shell script I am trying to get the product of odd numbers

mul=1
i=0
while [ $i -ne 10 ]
do
echo "Enter Number"
read num
if [ `expr $num % 2` -ne 0 ]
then
mul=`expr $mul*$num`
fi
i=`expr $i + 1`
done
echo mul of odd numbers = $mul
this is what i tried...its showing output as 1*3*5*7*9
pls correct the error here
Thanks in advance
"*" has a special meaning, hence you need to escape it and need to have a space between the two variables like:
mul=`expr $mul \* $num`
Note aside- Use of back ticks are discouraged and you may want to use something instead like:
mul=$(expr $mul \* $num)
Since your don't provide some details (see my comment above) I can't guarantee this answers your question and produces the desired result. This assumes your shell is bash. Please inform me and I'll edit the answer accordingly.
Consider the changes below. The relevant part is the change from expr ... to $(( ... )), which is bash's built-in arithmetic expression evaluator.
#!env bash
MUL=1
I=0
while [ $I -ne 10 ]
do
echo "Enter Number"
read NUM
if [[ $(($NUM % 2)) -ne 0 ]] ; then
MUL=$(($MUL * $NUM))
fi
I=$(($I + 1))
done
echo MUL of odd numbers = $MUL
This produces the following output:
$ sh foo.sh
Enter Number
1
Enter Number
2
Enter Number
3
Enter Number
4
Enter Number
5
Enter Number
6
Enter Number
7
Enter Number
8
Enter Number
9
Enter Number
0
MUL of odd numbers = 945

What is the common name of this "binary" approach to counting? How should it be done?

The following script is a study from my work with a script that I use in order to scrape web pages with links to an unknown number of sequentially numbered files. In order to create as few useless requests as possible, the scraping script guesses the number of files in a "binary" way.
This "study" script optionally takes a guess as $1 as a point of departure in order to find the number of .txt files in a directory. As I was writing it, i had the feeling of reinventing the wheel.
What is this way of counting called? How could it be done less clumsily in Bash?
#!/bin/bash
set_SEARCH()
{
DIVIDED=$SEARCH
SEARCH=$(($SEARCH * 2))
}
increase_SEARCH()
{
SEARCH=$(($SEARCH + $DIVIDED))
DIVIDED=$(($DIVIDED / 2))
#echo $DIVIDED
}
decrease_SEARCH()
{
SEARCH=$(($SEARCH - $DIVIDED))
DIVIDED=$(($DIVIDED / 2))
#echo $DIVIDED
}
test_SEARCH()
{
while [ -f "$(($SEARCH * 2)).txt" ]
do
set_SEARCH
done
if [ -f "${SEARCH}.txt" ]
then
echo "Trying $SEARCH"
if [ "$DIVIDED" = 0 ]
then
NUMBER=$(($SEARCH + 0))
echo "The number is $NUMBER"
exit
fi
increase_SEARCH
test_SEARCH
else
echo "Trying $SEARCH"
if [ "$DIVIDED" = 0 ]
then
NUMBER=$(($SEARCH - 1))
echo "The number is $NUMBER"
exit
fi
decrease_SEARCH
test_SEARCH
fi
}
SEARCH=2
while [[ "$SEARCH" -le "$1" ]] ;
do
SEARCH=$(($SEARCH * 2))
done
DIVIDED=$(($SEARCH / 2))
test_SEARCH
This is binary search with unknown upper bound. Knowing this, we can easily write/adapt implementations:
upper=42 # an arbitrary number >= 1
lower=0
while [[ -f $upper.txt ]]
do
lower=$upper
(( upper*= 2 ))
done
while (( lower < upper-1 ))
do
middle=$(((lower+upper)/2))
if [[ -f $middle.txt ]]
then
lower=$middle
else
upper=$middle
fi
done
echo "The number is $lower"

Resources