Nested while loop not working in Bash - bash

Beginner here so bear with me. I am trying to compare homework submissions from a solution file and a student submission file. The contents of each file have three problems, one per line:
problem 1 code
problem 2 code
problem 3 code
I want to compare each line in the solution with the corresponding line in the students submission. I am using a for loop to run through each student file and a nested while loop to run through each line of the solution file and student file. For some reason the script is completely ignoring the while loop. I have put echoes between each line to see where the problem is(the echo $solution and echo $submission is just to check to see if the path is correct):
for submission in /home/myfolder/submissions/*
do
echo 1
solution=$(echo /home/myfolder/hwsolution/*)
echo 2
echo $solution
echo $submission
while read sans <&1 && read sol <&2
do
echo 3
echo Student awnser is: $sans
echo Solution is: $sol
echo 4
done 1<$(echo $submission) 2<$(echo $(echo $solution))
echo 5
done
When I run it I get:
1
2
/home/myfolder/hwsolution/solution
/home/myfolder/submissions/student1
5
1
2
/home/myfolder/hwsolution/solution
/home/myfolder/submissions/student2
5
1
2
/home/myfolder/hwsolution/solution
/home/myfolder/submissions/student3
5

It's not ignoring the while loop -- you're redirecting the file descriptors used for stdout and stderr, so echo can't write to the console within it.
for submission in /home/myfolder/submissions/*; do
solutions=( /home/myfolder/hwsolution/* )
if (( ${#solutions[#]} == 1 )) && [[ -e ${solutions[0]} ]]; then
solution=${solutions[0]}
else
echo "Multiple solution files found; don't know which to use" >&2
printf ' - %q\n' "${solutions[#]}" >&2
exit
fi
while read sans <&3 && read sol <&4; do
echo "Student awnser is: $sans"
echo "Solution is: $sol"
done 3<"$submission" 4<"$solution"
done
The most immediate change is that we're redirecting FD3 and FD4, not FD1 and FD2.

Related

Bash Script stuck looping

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 [[.

Increment Number in ZSH script

I am trying to create directories based off of the number of files I have in a directory and my approach is a loop.
I am keeping track of the number by a number variable and want to increment it as so after each even number, create a directory.
This is my code so far:
#!/bin/zsh
LOOP=$(ls 2021/*.xlsx)
totalFiles=$(ls 2021/*.xlsx | wc -l)
directoryCount=0
count=0
for FILE in ${LOOP[#]}; do
echo "BEFORE: $count"
count=$((count+1))
echo "AFTER: $count"
echo $FILE
done
echo "TOTAL FILE: $totalFiles"
echo $count
and the output I get is:
BEFORE: 0
AFTER: 1
2021/*Tanner2103.xlsx
2021/*Tanner2104.xlsx
2021/*Tanner2105.xlsx
2021/*Tanner2106.xlsx
TOTAL FILE: 4
1
I dont understand why count only increments once but the loop is obviously has more iterations than that.
So basically since there are 4 files, I want to split them up into 2 directories, Ill do the logic for that later. But for now Im just trying to get the directory code working.
Ok I feel silly. I thought that ls would come back as an iterable but its actual only return one iteration as a whole.
This is my updated code and it updating like I want.
totalFiles=$(ls 2021/*.xlsx | wc -l)
count=0
for FILE in 2021/**/*(.); do
echo "BEFORE $count"
echo "$FILE"
((count++))
echo "AFTER $count"
done
echo "TOTAL FILE: $totalFiles"
echo $count
result:
BEFORE 0
2021/*Tanner2103.xlsx
AFTER 1
BEFORE 1
2021/*Tanner2104.xlsx
AFTER 2
BEFORE 2
2021/*Tanner2105.xlsx
AFTER 3
BEFORE 3
2021/*Tanner2106.xlsx
AFTER 4
TOTAL FILE: 4
4

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.

Select in loop don't ask for input

I'm trying to ask a question in a loop (ask confirmation to treat each file in a directory, for instance).
This piece of code work perfectly :
PS3="Dummy question ? "
select CHOICE_MADE in "Ans1" 'Ans2' 'Ans3'; do
if [[ -n ${CHOICE_MADE} ]]; then
printf "Choice made : %s\n" "${CHOICE_MADE}"
break;
fi
done
I need to loop this question (Note I name the variable in loop, to avoid using default $REPLY variable) :
while read -r TOTO; do
PS3="Dummy question ? "
select CHOICE_MADE in Ans1 Ans2 Ans3; do
if [[ -n ${CHOICE_MADE} ]]; then
printf "Choice made : %s\n" "${CHOICE_MADE}"
break;
fi
done
done < <(echo Step1 Step2)
Select doesn't prompt anything.
I don't want to use whiptail (which masks script previous output), dialog (need to be installed on debian) or any gui.
I know I could use read to solve my problem, but I would like keeping select loop.
Thanks in advance for your help.
You have to note two things in order to make it works.
1) Bad use of `stdin`
First of all, passing < (echo Step1 Step2) to your while cycle, you are making use of stdin and that's why the prompt does not wait for any input at all (It already has it!).
You can get rid of it by using file descriptors:
exec 3< <(echo Step1 Step2) ### Create fd "3" and put output of echo in it
while read -r TOTO <&3; do
PS3="Dummy question ? "
select CHOICE_MADE in Ans1 Ans2 Ans3; do
if [[ -n ${CHOICE_MADE} ]]; then
printf "Choice made : %s\n" "${CHOICE_MADE}"
break
fi
done
done
exec 3>&- ### clean fd 3
I left unchanged the rest of code.
2) Bad use of `read`
Another thing you have to think of, is that read -r will read line by line what you give him, and a simple echo Step1 Step2 will produce only a single line, so you will cycle through just a single occurrence.
In order to get rid of this unwanted behaviour you will have to use other solutions.
One solution I can think of at the moment is heredoc:
exec 3<< EOF
Step1
Step2
EOF
while read -r TOTO <&3; do
PS3="Dummy question ? "
select CHOICE_MADE in Ans1 Ans2 Ans3; do
if [[ -n ${CHOICE_MADE} ]]; then
printf "Choice made: %s\n" "${CHOICE_MADE}"
break
fi
done
done
exec 3>&- ### clean fd 3
Note that using several heredoc is not advisable and best practices suggest to use actual files instead, because a script should contains logic, not data.

Reading multiple lines to redirect

currently I have a file named testcase and inside that file has 5 10 15 14 on line one and
10 13 18 22 on line two
I am trying to bash script to take those two inputs line by line to test into a program. I have the while loop comment out but I feel like that is going in the right direction.
I was also wondering if is possible to know if I diff two files and they are the same return true or something like that because I dont now if [["$youranswer" == "$correctanswer"]] is working the way I wanted to. I wanted to check if two contents inside the files are the same then do a certain command
#while read -r line
#do
# args+=$"line"
#done < "$file_input"
# Read contents in the file
contents=$(< "$file_input")
# Display output of the test file
"$test_path" $contents > correctanswer 2>&1
# Display output of your file
"$your_path" $contents > youranswer 2>&1
# diff the solutions
if [ "$correctanswer" == "$youranswer" ]
then
echo "The two outputs were exactly the same "
else
echo "$divider"
echo "The two outputs were different "
diff youranswer correctanswer
echo "Do you wish to see the ouputs side-by-side?"
select yn in "Yes" "No"; do
case $yn in
Yes ) echo "LEFT: Your Output RIGHT: Solution Output"
sleep 1
vimdiff youranswer correctanswer; break;;
No ) exit;;
esac
done
fi
From the diff(1) man page:
Exit status is 0
if inputs are the same, 1 if different, 2 if trouble.
if diff -q file1 file2 &> /dev/null
echo same
else
echo different
fi
EDIT:
But if you insist on reading from more than one file at a time... don't.
while read -d '\t' correctanswer youranswer
do
...
done < <(paste correctfile yourfile)

Resources