I want a variable to be incremented by 1 per second.
#!/bin/bash
var=0
watch -n 1 echo "$((var++))"
Output (after few seconds) :
Every 1.0s: echo 0
0
It didn't change on screen but when i enter echo "$var"
output is 1.Why?
I want a variable to be incremented by 1 per second.
#!/bin/bash
var=0
watch -n 1 echo "$((var++))"
Output (after few seconds) :
Every 1.0s: echo 0
0
It didn't change on screen but when i enter echo "$var"
output is 1.Why?
It's not really easy to do this with
watch
, but possible. I'll explain it below.However, if you only want a counter that increments once per second, and don't really have to use
watch
for any other reason, you can simply solve this with a shell loop and asleep
:If it is not possible to convert your
watch
command into a loop like this, or of course if you are interested in why it doesn't work the way you tried to do it, read on.For one, your first problem here is that in
Bash will first expand your arithmetic expression and then run the command, so it actually runs
and then increments
var
once, which is why you get1
as value afterwards.Now you could put it in single quotes instead of double quotes, but then no expansion is going to happen at all and it will output
$((var++))
literally all the time. We can put it througheval
though to evaluate the string again. This evaluation will then happen every second.Because
watch
usessh
by default, we need to use a different arithmetic syntax, as it doesn't support++
:$(( var += 1))
- Note that this will increment the variable first and then return the incremented value, unlike$((var++))
which first returned the original value and then incremented the variable.This will unfortunately print
1
inside the watch, and the final value ofvar
will stay0
.Why? The watched command runs in a subshell. Subshells have their own, isolated environments and variables.
You can export your variable of a parent shell (the one where you type) to its subshells (those inside which the watched commands run) by running
export var
once before thewatch
, but that only gives them read-access. Every change to the variable, like the increment, is still only happening inside the subshell and not propagated to the parent.If you
export var
as described, the watched output will constantly be whatever starting value you have set + 1, but the final value of the variable will remain the starting value.So, we learned that there is really no way to directly pass a variable from a subshell to its parent, so whatever happens inside the watched command can neither affect the variables and environment of your parent shell, nor can the changes persist across calls of the same command by
watch
.We need some kind of temporary storage that is not bound to the shell environment where we can read and write our current counter variable. For example, a file:
This creates a temp file with random name in
/tmp
, remembers its name in the exported shell variable$temp
and writes your starting value 0 to it.Now inside your watch, you have to read your variable from that file, then you can increment it and do whatever you want, but in the end you must write your changed value back to the file:
This will finally make your watch count up. However, don't forget that you have to read the last value from the file into your parent shell variable again, if you still want to access it after your watch is over, and also don't forget to delete the temp file again: