Я хочу, чтобы переменная была увеличена 1 в секунду.
#!/bin/bash
var=0
watch -n 1 echo "$((var++))"
Вывод (после нескольких секунд):
Every 1.0s: echo 0
0
Это не изменилось на экране, но когда я вхожу echo "$var"
вывод равняется 1. Почему?
Не действительно легко сделать это с watch
, но возможный. Я объясню это ниже.
Однако, если Вы только хотите счетчик, который увеличивает однажды в секунду и не должен действительно использовать watch
по любой другой причине можно просто решить это с циклом оболочки и a sleep
:
var=0
while sleep 1 ; do echo "$((var++))" ; done
Если не возможно преобразовать Ваш watch
команда в цикл как это, или конечно если Вы интересуетесь тем, почему она не работает способ, которым Вы пытались сделать это, продолжать читать.
Для одного Ваша первая проблема здесь - это в
watch -n 1 echo "$((var++))"
Bash сначала развернет Ваше арифметическое выражение и затем выполнит команду, таким образом, это на самом деле будет работать
watch -n 1 echo 0
и затем инкременты var
однажды, который является, почему Вы добираетесь 1
как оценивают впоследствии.
Теперь Вы могли поместить его в одинарные кавычки вместо двойных кавычек, но затем никакое расширение не собирается произойти вообще, и это произведет $((var++))
буквально все время. Мы можем поместить его через eval
хотя оценить строку снова. Эта оценка будет затем происходить каждую секунду.
Поскольку watch
использование sh
по умолчанию мы должны использовать другой арифметический синтаксис, поскольку он не поддерживает ++
: $(( var += 1))
- Обратите внимание, что это увеличит переменную сначала и затем возвратит увеличенное значение, в отличие от этого, $((var++))
который сначала возвратил исходное значение и затем увеличил переменную.
watch -n 1 eval 'echo $((var += 1))'
Это, к сожалению, распечатает 1
в часах и окончательном значении var
останется 0
.
Почему? Следившая команда работает в подоболочке. Подоболочки имеют свои собственные, изолированные среды и переменные.
Можно экспортировать переменную родительской оболочки (тот, где Вы вводите) к его подоболочкам (те внутри, который следившие выполненные команды) путем выполнения export var
однажды watch
, но это только дает им доступ для чтения. Каждое изменение в переменной, как инкремент, все еще только происходит в подоболочке и не распространенное к родителю.
Если Вы export var
как описано, наблюдаемый вывод постоянно будет любым начальным значением, которое Вы установили + 1, но окончательное значение переменной останется начальным значением.
Так, мы узнали, что нет действительно никакого способа непосредственно передать переменную от подоболочки до ее родителя, так что бы ни случилось в следившей команде не может ни влиять на переменные и среду Вашей родительской оболочки, и при этом изменения не могут сохраниться через вызовы той же команды watch
.
Нам нужно некоторое временное хранение, которое не связывается со средой оболочки, где мы можем считать и записать нашу текущую переменную счетчика. Например, файл:
export temp=$(tempfile)
echo 0 > "$temp"
Это создает временный файл со случайным именем в /tmp
, помнит его имя в экспортируемой переменной оболочки $temp
и пишет Ваше начальное значение 0 в него.
Теперь в Ваших часах, необходимо считать переменную из того файла, затем можно увеличить его и сделать то, что Вы хотите, но в конце необходимо записать измененное значение обратно в файл:
watch -n 1 'read var < "$temp" ; echo "$((var+=1))" ; echo "$var" > "$temp"'
Это наконец проведет Ваш подсчет часов. Однако не забывайте, что необходимо считать последнее значение из файла в родительскую переменную оболочки снова, если Вы все еще хотите получить доступ к нему после того, как Ваши часы закончены, и также не забывайте удалять временный файл снова:
read var < "$temp"
rm "$temp"