Постепенное увеличение переменной командой часов

Я хочу, чтобы переменная была увеличена 1 в секунду.

#!/bin/bash
var=0
watch -n 1 echo "$((var++))" 

Вывод (после нескольких секунд):

Every 1.0s: echo 0
0

Это не изменилось на экране, но когда я вхожу echo "$var" вывод равняется 1. Почему?

0
задан 19 August 2018 в 09:16

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"
3
ответ дан 28 October 2019 в 03:04

Другие вопросы по тегам:

Похожие вопросы: