I have the following bash function that has me boondoggled. If I enter the following in the zenity boxes…
employeeid = 2
categoryid = 3
I get the following: 2 3
if…
employeeid =
categoryid = 3
After a second zenity window opens I enter 2 and I get the following: 2 3
However when I enter
employeeid = 2
categoryid =
No additional Zenity window opens and I get the following: 2
What I really want to end up with is 2,3 after the tests have run.
Does anyone know what is wrong here?
#!/bin/bash
num(){
emp=$(echo "$1" | awk -F, -v OFS=, '{print $1 "," $2}')
IFS=, read -ra array1 <<<"$emp"
p=$(for i in "${array1[@]}"
do
if [[ "${i}" =~ ^[0-9]+$ ]]; then
out="${i}"
elif
[[ "${i}" = NULL ]]; then
out="${i}"
else local var2
until [[ ${var2} =~ ^[0-9]+$ ]] || [[ ${var2} = NULL ]]; do
var2="$(zenity --forms --title="table salaries_wages" --text "Add a number" --separator="," \
--add-entry="WARNING! You either forgot to enter or didn't enter a number. Please enter a valid number: ")"
done
out="${var2}"
fi
echo "$out"
done)
echo "$p"
}
input="$(zenity --forms --title="table salaries_wages" --text="Add a new salaries_wages entry" --separator="," \
--add-entry="ENTER employeeid: " \
--add-entry="ENTER categoryid: ")"
num "$input"
Best Answer
Your assumption of how
read
works is different than howread
actually works. Run this code in Bash:You will get
2
,2
,1
. The last number stands out. It means that trailing separator (,
in this case) is treated byread
more like a terminator: the empty field after it is not read into the array and the array ends up being one element too short. If this happens in your code thenfor i in "${array1[@]}"
will run the loop for just the first field.The fix may be to introduce an extra
,
as the trailing terminator on purpose. Thenread
will never read the third field, but it will always read two fields (even if the second one is empty). See the difference when I add an extra,
:The output is
2
each time.To fix your code this way use
<<<"$emp,"
instead of<<<"$emp"
.If you fix the code then it will misbehave when the
else
block is run more than once (i.e. when both fields are initially invalid); because you're reusing thevar2
variable again.I guess you used
local var2
to avoid this, butlocal
makes the variable local in a function, not in anelse
block or in a single iteration of afor
loop. You're reusingvar2
in the same instance of thenum
function.You're calling the function once. Inside it
var2
is always the samevar2
,local
only makes it distinct from anyvar2
outside this very invocation of the function. If you usedvar2
outside of the function, the one in the function would be distinct. If you invokednum
more than once, each invocation would use its own distinctvar2
. Neither of these happens. You're calling the function once and reusing the variable there. When two fields are invalid the variable is used for the first field and then reused for the second field.But if you rebuild your code so some function (e.g.
validate
) is called from within the loop:and use
local var2
in thevalidate
function, thenvar2
in each invocation of the function will be distinct. This is howlocal
can help. In each loopvalidate
will be called anew, its local variable(s) will be initialized fresh without any connection to other variable(s) with the same name(s). However you will still be able to reuse a local variable inside the function in a way that breaks something (similarly to how you're currently reusingvar2
in thenum
function).After I wrote the above, a added an example to the already linked answer.
Note
p=$(stuff); echo "$p"
is almost equivalent toecho "$(stuff)"
, which almost always should be juststuff
. Please read this answer where it elaborates onvar=$(stuff); echo "$var"
.