Bash incrementation breaking scripts - bash

I'm having a weird issue incrementing a bash variable that seems
to be breaking after my first attempt at incremntation that I cannot
pin down, here is a sample of what I am doing and the debug output,
anyone see any reason this should NOT work?
I am currently on GNU bash, version 4.2.45(1)-release (i686-pc-linux-gnu)
#!/bin/bash
set -ex
declare -i elem=0
echo $elem # 0
(( elem++ )) # breaks
echo $elem # 1 but never encountered
while IFS=$'\n' read -r line || [[ -n "$line" ]]; do
(( elem++ ))
echo $elem
done <"${1}" # foo\nbar\nbaz
Output
./incr.sh test
+ declare -i elem=0
+ echo 0
0
+ (( elem++ ))
The weirdest part is by changing the initial incrementor to (( elem+=1 ))
the entire program increments correctly, this seems extremely buggy to the eye...
#!/bin/bash
set -ex
declare -i elem=0
echo $elem
(( elem+=1 ))
echo $elem
while IFS=$'\n' read -r line || [[ -n "$line" ]]; do
(( elem++ ))
echo $elem
done <"${1}" # foo\nbar\nbaz
Output
+ declare -i elem=0
+ echo 0
0
+ (( elem+=1 ))
+ echo 1
1
+ IFS='
'
+ read -r line
+ (( elem++ ))
+ echo 2
2
+ IFS='
'
+ read -r line
+ (( elem++ ))
+ echo 3
3
+ IFS='
'
+ read -r line
+ (( elem++ ))
+ echo 4
4
+ IFS='
'
+ read -r line
+ [[ -n '' ]]

set -e makes your script exit when any command returns failure.
(( 0 )), and equivalently elem=0; (( elem++ )) returns failure.
Therefore, the script exits.
If you set -e and want to run commands whose status you don't care, about, you can use
(( elem++ )) || true

Related

Error getting in the second if statement in bash script

n=20
x=3
count=0
flag=0
i=1
declare -a arr[n+1]
for (( j=0;j<=n;j++ ))
do
arr+=(0)
done
#echo "${arr[#]}"
while [[ $count -ne $n ]]
do
if [[ $i -le $n ]]
then
if [[ ${arr[$i]} -eq '0' ]]
then
echo "Value is ${arr[$i]}"
#${arr[$(i-1)]}= (( ${arr[$i-1]++} ))
${arr[$i]}+=${arr[$i]}
echo " "
#echo -n "${arr[$i]}"
echo -n " $i"
count=$(( count+1 ))
i=$(( i+1+x ))
else
i=$(( i+1 ))
fi
else
i=$(( i-n ))
flag=$(( flag+1 ))
fi
done
echo " "
echo "No of round : $flag"
This is the whole code, I've tried to print numbers that follows this: n=20 is the number of elements and x=3 is the number that we have to avoid. For example,
20
3
1,5,9,13,17,2,6,10,14,18,3,7,11,15,19,4,8,12,16,20,
3
But, the problem is that my second if condition is not fulfilling, if ignores the condition. Above example is for the C++, but in bash script, 2nd if statement isn't working. This can be because syntax is wrong. So can you please help me to find the mistakes.
Output of the above code:
output
${arr[$i]}+=${arr[$i]}
This is incorrect. $ should not be used when you assign the value.
If you want to double the value, replace this string with the following:
arr[$i]=$(( ${arr[$i]} + ${arr[$i]} ))
Or what you want to do there?

Bash Script - sourceFile not printing to targetFile

The following code should be printing the contents of the sourceFile to targetFile, line by line, with each line having in front 'wireless-key s:', but it only prints 'wireless-key s:' to the targetFile.
#!/bin/bash
sourceFile="file1.log"
targetFile="/etc/network/interfaces"
numLines="$(wc -l < "${sourceFile}")"
counter=5
lineNumber=5
if (( counter >= "$numLines" )) || [[ ! -f "${sourceFile}" ]]; then
echo "invaild file" >82; exit 1
fi
while [ "$counter" -le "$numLines" ]; do
sed -i "${lineNumber} s/.*/wireless-key s: $(sed -n ${counter}p <<< " ${sourceFile}")/" "${targetFile}"
counter=$((counter + 1))
done
Thank you
perhaps a different approach...
awk '{print NR,"wireless_key",$0}' srcfile > dstfile

Bash FIFOs not working as expected - how to debug it

I was given the following assignment. We have a prebuilt GUI in a binary form, kept in $GAME_BIN. I have to write a script which connects the GUI with the AI engine. This is my code, which is pretty self descriptive. We have in this case: GAME_BIN=./sredniowiecze_gui, ai1=./idle_ai.sh, ai2=./idle_ai.sh
#!/bin/bash
# arguments parsing and setting $args - not posting this here
gui_outpipe=$(mktemp -u)
gui_inpipe=$(mktemp -u)
ai1_outpipe=$(mktemp -u)
ai1_inpipe=$(mktemp -u)
ai2_outpipe=$(mktemp -u)
ai2_inpipe=$(mktemp -u)
mkfifo $gui_outpipe $gui_inpipe $ai1_outpipe $ai1_inpipe $ai2_inpipe $ai2_outpipe
printinit 1 > $ai1_inpipe &
printinit 2 > $ai2_inpipe &
$GAME_BIN $args < $gui_inpipe &
$ai1 < $ai1_inpipe > $ai1_outpipe &
$ai1 < $ai2_inpipe > $ai2_outpipe &
while true; do
echo "Started the loop"
while true; do
read line < $ai1_outpipe || echo "Nothing read"
echo $line
if [[ $line ]]; then
echo "$line" > $gui_inpipe
echo "$line" > $ai2_inpipe
if [[ "$line" == "END_TURN" ]]; then
break
fi
fi
done
sleep $turndelay
while true; do
read line < $ai2_outpipe || echo "nothing read"
echo $line
if [[ $line ]]; then
echo "$line" > $gui_inpipe
echo "$line" > $ai2_inpipe
if [[ "$line" == "END_TURN" ]]; then
break
fi
fi
done
sleep $turndelay
done
wait
I created a simple idle AI contained in idle_ai.sh
#!/bin/sh
while true; do
echo END_TURN
done
Then the END_TURN message from the GUI is not received at all. On the other hand, the second END_TURN in line (*) is not received by the script. If I use my own C-written AI - very long code, not posting it here, no information from the AI is received in the second run of the while loop
I have absolutely no idea how to debug it.
Since I'm not eager to run binaries unsandboxed, I'm calling the script by firejail ./game.sh [irrelevant parameters]
EDIT after adding set -x the output is
INIT 10 3 1 1 1 3 9
+ [[ -n ./idle_ai.sh ]]
+ [[ -n '' ]]
+ [[ -n ./idle_ai.sh ]]
+ printinit 1
+ ./sredniowiecze_gui -human2
+ true
+ echo 'Started the loop'
Started the loop
+ true
+ read line
+ ./idle_ai.sh
+ echo 'INIT 10 3 1 1 1 3 9'
+ echo END_TURN
END_TURN
+ [[ -n END_TURN ]]
+ echo END_TURN
+ [[ END_TURN == \E\N\D\_\T\U\R\N ]]
+ break
+ true
+ read line
+ echo MOVE 5 9 5 8
MOVE 5 9 5 8
+ [[ -n MOVE 5 9 5 8 ]]
+ echo 'MOVE 5 9 5 8'
In AI vs AI mode:
INIT 10 3 1 1 1 3 9
+ [[ -n ./idle_ai.sh ]]
+ [[ -n ./idle_ai.sh ]]
+ printinit 1
+ printinit 2
+ ./sredniowiecze_gui
+ ./idle_ai.sh
+ true
+ echo 'Started the loop'
Started the loop
+ echo 'INIT 10 3 1 1 1 3 9'
+ true
+ read line
+ ./idle_ai.sh
+ echo 'INIT 10 3 2 1 1 3 9'
+ echo END_TURN
END_TURN
+ [[ -n END_TURN ]]
+ echo END_TURN
+ echo END_TURN
+ [[ END_TURN == \E\N\D\_\T\U\R\N ]]
+ break
+ sleep 1
+ true
+ read line
+ echo END_TURN
END_TURN
+ [[ -n END_TURN ]]
+ echo END_TURN
+ echo END_TURN
+ [[ END_TURN == \E\N\D\_\T\U\R\N ]]
+ break
+ sleep 1
+ true
+ echo 'Started the loop'
Started the loop
+ true
+ read line
EDIT2
I did the suggested changes, now my code is:
printinit 1 > $ai1_inpipe &
printinit 2 > $ai2_inpipe &
$GAME_BIN $args < $gui_inpipe &
$ai1 < $ai1_inpipe > $ai1_outpipe &
echo $!
$ai2 < $ai2_inpipe > $ai2_outpipe &
echo $!
while true; do
echo "Started the loop"
while true; do
read -u3 line || echo "Nothing read"
echo $line
if [[ $line ]]; then
echo "$line" > $gui_inpipe
echo "$line" > $ai2_inpipe
if [[ "$line" == "END_TURN" ]]; then
break
fi
fi
done
sleep $turndelay
while true; do
read -u4 line || echo "nothing read"
echo $line
if [[ $line ]]; then
echo "$line" > $gui_inpipe
echo "$line" > $ai1_inpipe
if [[ "$line" == "END_TURN" ]]; then
break
fi
fi
done
sleep $turndelay
done 3<$ai1_outpipe 4<$ai2_outpipe
And now the script gets stuck on the echo "$line" > $ai1_inpipe line, although the $ai2 process is still running.
EDIT3. Now the log with set -x is
INIT 10 3 1 1 1 3 9
+ [[ -n ./idle_ai.sh ]]
+ [[ -n ./idle_ai.sh ]]
+ printinit 1
+ printinit 2
+ ./sredniowiecze_gui
+ echo 26
26
+ ./idle_ai.sh
+ echo 'INIT 10 3 1 1 1 3 9'
+ echo 27
27
+ ./idle_ai.sh
+ echo 'INIT 10 3 2 1 1 3 9'
+ true
+ echo 'Started the loop'
Started the loop
+ true
+ read -u3 line
+ echo END_TURN
END_TURN
+ [[ -n END_TURN ]]
+ echo END_TURN
+ echo END_TURN
+ [[ END_TURN == \E\N\D\_\T\U\R\N ]]
+ break
+ sleep 1
+ true
+ read -u4 line
+ echo END_TURN
END_TURN
+ [[ -n END_TURN ]]
+ echo END_TURN
+ echo END_TURN
If you add an echo FOO before and after the call, like this:
echo FOO
echo "$line" > $ai1_inpipe
echo BAR
then echo FOO is executed and echo BAR not.
You're using read < input, which sucks all the input and only uses the first line.
Instead of doing that, you should have read read from open file descriptors, like this:
EDIT: Same thing with writing to the fifo files with echo
while true; do
echo "Started the loop"
while true; do
read -u3 line || echo "Nothing read"
...
echo "$line" >&5
echo "$line" >&6
...
done
sleep $turndelay
while true; do
read -u4 line || echo "nothing read"
...
echo "$line" >&5
echo "$line" >&7
...
sleep $turndelay
done 3<$ai1_outpipe 4<$ai2_outpipe 5>$gui_inpipe 6>$ai2_inpipe 7>$ai1_inpipe
See these links for more help on the topic:
BashGuide Named pipes
BashFAQ 085

Bash: Capture newline with `read`

I have a loop that I want to break on any user input.
Contrived example – a timer that counts down from 10:
counter=10
while (( counter > 0 )) && [[ -z $input ]]; do
printf '\rRestart in %s seconds' "$counter"
read -n 1 -t 1 input
(( counter-- ))
done
echo "Completed"
This works appropriately for all user input except for Enter, and I fully expect that Enter will be the most common input.
How can I capture Enter using Bash's read command?
The bash manual says that read returns success (return code zero) unless it times out. So how about this:
for (( counter=10 ; counter > 0 ; counter-- )); do
printf "\rRestart in %s seconds " $counter
read -n 1 -t 1 input && break
done
echo "Completed"
Tell read to use NUL as its delimiter:
IFS='' read -r -d '' -n 1 -t 1 input
So, in full context:
counter=10; input=''
while (( counter > 0 )) && [[ -z $input ]]; do
printf "\rRestart in %s seconds" "$counter"
IFS='' read -n 1 -t 1 -d '' input
(( counter-- ))
done

how to extract numbers from this echo into separate variables?

Sorry about bits and snippit of information
So I am writing an average shell script program
so if use inputs
echo 1 3, .... | sh get_number
I would have to pull the numbers seperated by spaces from echo to be
var1 = 1, var2= 3, etc.
I tried
#!/bin/sh
sum=0
for i in $*
do
sum=`expr $sum + $i`
done
avg=`expr $sum / $n`
echo Average=$avg
but doesnt work....
do I include a read here?
also how would I do
sh get_number <file1>, <file2>... to grab numbers in them and sum them
in shell script?
Thanks
Sounds like you are looking for the read shell builtin:
% echo "1 2 3 4" | read a b stuff
% echo $b
2
% echo $stuff
3 4
To fix up your code:
for i in $*; do
sum=$(( sum + i ))
n=$(( n + 1 ))
done
echo "Average=$(( sum / n ))"
#!/bin/sh
while [ $# -gt 0 ]; do
(( i++ ))
(( sum += $1 ))
shift
done
echo "Average=$(( sum/i ))"
Note: This fails in dash which is the closest shell I could find to a real sh.
An example of reading values from files passed as command line arguments or from lines read from stdin:
add_to_sum() {
set $*
while [ $# -gt 0 ]; do
I=`expr $I + 1`
SUM=`expr $SUM + $1`
shift
done
}
I=0
SUM=0
if [ $# -gt 0 ]; then
# process any arguments on the command line
while [ $# -gt 0 ]; do
FILE=$1
shift
while read LINE; do
add_to_sum "$LINE"
done < "$FILE"
done
else
# if no arguments on the command line, read from stdin
while read LINE; do
add_to_sum "$LINE"
done
fi
# be sure not to divide by zero
[ $I -gt 0 ] && echo Average=`expr $SUM / $I`

Resources