Bash Script stuck looping - bash

I'm trying to write a script that runs another script which fails rarely until it fails.
Here is the script that fails rarely:
#!/usr/bin/bash
n=$(( RANDOM % 100 ))
if (( n == 42 )) ; then
echo "$n Something went wrong"
>&2 echo "The error was using magic numbers"
exit 1
fi
echo "$n Everything went according to plan"
Here is the script that should run the previous script until it fails:
#!/usr/bin/bash
script_path="/tmp/missing/l2q3.sh"
found=0
counter=0
while (( $found == 0 )); do
output=(bash $script_path)
if (( $output == 42 Something went wrong )); then
found=1
fi
((counter++))
if (( $found == 1 )); then
echo "Number 42 was found after $counter tries"
fi
done
when I try running the second script I get stuck in an infinite loop saying there is a syntax error on line 11 and that something is wrong with 42 Something went wrong. I've tried with "42 Something went wrong" aswell and still stuck in a loop.

The form (( )) is arithemetic only, so you cannot test a string inside.
To test a string, you have to use the [[ ]] version:
[[ $output == "42 Something went wrong" ]] && echo ok
ok

You can use the program execution as the test for a while/until/if (etc.)
Assuming your script returns a valid 0 error code on success, and nonzero on any other circumstance, then -
$: cat tst
#!/bin/bash
trap 'rm -fr $tmp' EXIT
tmp=$(mktemp)
while /tmp/missing/l2q3.sh >$tmp; do let ++ctr; done
grep -q "^42 Something went wrong" $tmp &&
echo "Number 42 was found after $ctr tries"
In use:
$: ./tst
The error was using magic numbers
Number 42 was found after 229 tries

here are 3 steps to move forward.
add a return value at the end of the first script
exit 0
make your first script has executable rights
$ chmod a+x /tmp/missing/12q3.sh
instead of while loop you may use until, which would run until it returns success i.e. 0
until /tmp/missing/l2q3.sh; do ((counter++)) done
for other if statements please use square brackets [ single or double [[.

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

read -p inside the function in bash script

I am trying to write the function including read -p, however, for some reason the read -p always show first before other command, although other commands are before read -p. Here is my code:
function try {
temp=10
echo "$temp"
while [[ $temp -gt 0 ]]
do
read -p "what num do you want?" num
echo "$num"
temp=$((temp - num))
echo $temp
done
}
run=`try`
echo "$run"
As the above code, I expected to see value of temp before statement "what num do you want?". However, here what I got:
what num do you want?5
what num do you want?5
10
5
5
5
0
Can anyone help me to solve my problem. Thanks in advance
Duplicate each line in your function which contains echo and append >&2 to the new lines to redirect stdout to stderr.

Arithmetic comparison does not work in Bash

So, I have a script for moving files from one directory to another. I want to check whether the moving was successful or not, so I use this block of code:
...
mv "${!i}" "$dirname" 2>/dev/null
echo $? # Check MV exit code
if (( $?==1 ))
then
...
The problem is that whether moving was successful or not, then does not work. If I do this instead
if (( $?==0 ))
it instead works in any case. I have read that it may be because $? is treated like a string, and strings have 0 value. However, if I change it to this
if (( $?=="1" ))
it does not work either. I have tried using [[ ... ]] and [ ... ] instead of (( ... )), and -eq instead of ==, removing and adding spaces, adding and removing quotes, but nothing worked.
What am I doing wrong? Maybe there is another way of responding to certain exit code?
The problem is here:
echo $?
if (( $?==1 ))
The first echo $? will echo the return value of your mv command; however, in the if statement, using $? again will give you the return value of the echo command! $? is always the return value of the last command. The last command is the echo and it is always succeeding so you are always getting a 0 return value in that if statement.
What you should do instead is save the value into a variable and then compare things to that variable:
mv "${!i}" "$dirname" 2>/dev/null
ret_val=$?
echo ${ret_val}
if (( ${ret_val}==1 ))
You can check the exit status of your command directly:
if mv "${!i}" "$dirname" 2>/dev/null; then
# Code for successful move
else
# Code for unsuccessful move
fi
Or, to keep the happy path less indented:
if ! mv "${!i}" "$dirname" 2>/dev/null; then
# Code for unsuccessful move
return 1 # Or maybe exit 1 if in a script, not a function
fi
# Code for successful move
As for how the exit status of echo messes up your code, Tyler's answer has that covered.

if statement always goes to the else

I just started learning Bash scripting and i have to do a program that separate between one bit map image to two (the image is broken), I already found on the web how to write loops and statements
but i don't know why my if statement is always goes to the else.
the if is modulo by 2 thats equals to 0
here is the following code
#!/bin/sh
OUTPUT="$(hexdump -v -e '/1 "%02X\n"' merge.bmp)"
echo $OUTPUT
vars=0
count=1
touch one
touch two
for i in $OUTPUT
do
if (($vars%2==0))
then
echo "1"
else
echo "2"
fi
vars=$((vars+count))
done
in the terminal the following error is
./q3.sh: 14: ./q3.sh: 2885%2==0: not found
2
i really don't know why the if always print 2
The shebang line is wrong, it should be:
#!/bin/bash
((expression)) is a bash extension, not available in sh.
The /bin/sh version of the (()) bashism is this:
if test $(($vars % 2)) -eq 0; then
echo "1"
...
fi
Since $(()) knows about variable names, you may even drop the dollar and write
if test $((vars % 2)) -eq 0; then
echo "1"
...
fi

Shell script: use while loop to check string contents using [ ]

I am trying to check a string that is output from a program, if the string matches a certain content, the while-loop will stop the program. At the same time, I need to count how many times the program has run:
x = "Lookup success" # this is supposed to be the output from the program
INTERVAL=0 # count the number of runs
while ["$x" != "Lookup failed"] # only break out the while loop when "Lookup failed" happened in the program
do
echo "not failed" # do something
$x = "Lookup failed" # just for testing the break-out
INTERVAL=(( $INTERVAL + 10 )); # the interval increments by 10
done
echo $x
echo $INTERVAL
But this shell script is not working, with this error:
./test.sh: line 9: x: command not found
./test.sh: line 12: [[: command not found
Could someone help me please? I appreciate your help.
You need spaces around the [ command name. You also need a space before the ] argument at the end of the command.
You also cannot have spaces around assignments in shell. And your assignment in the loop does not need a $ at the start.
x="Lookup success"
INTERVAL=0 # count the number of runs
while [ "$x" != "Lookup failed" ]
do
echo "not failed"
x="Lookup failed"
INTERVAL=(( $INTERVAL + 10 ))
done
echo $x
echo $INTERVAL
Not sure if there's a shell that would accept INTERVAL=((...)); my version of ksh and bash on two platforms does not. INTERVAL=$((...)) does work:
#!/bin/bash
x="Lookup success"
INTERVAL=0 # count the number of runs
while [ "$x" != "Lookup failed" ]
do
echo "not failed"
x="Lookup failed"
INTERVAL=$(( $INTERVAL + 10 ))
done
echo $x
echo $INTERVAL
Credits go to #JonathanLeffler. I'll appreciate up-votes so that next time I don't have to copy-paste others' solution for pointing out a simple typo (comment rights start with rep>=50).
Add a space after [ and before ].
Also, as Jonathan said, you cannot have space in assignments as well.

Resources