Что происходит, если Вы редактируете сценарий во время выполнения?

Информация текущего времени не хранится в файле, скорее она синхронизируется от Ваших аппаратных часов с /sbin/hwclock во время начальной загрузки. Видя эту дату, обычно средства, Ваши системные часы были сброшены, это часто вызывается неисправной батареей на Вашей системной плате (батарея CMOS),

Дата 31 декабря 1969 является опорной датой (представляющий это число в битах, это было бы всеми нулями), когда Ваша система сбрасывает системные часы, это сбрасывается к опорной дате (все нули).

Как устранить проблему

  1. Установите свое текущее время с date (направления находятся в man date)
  2. Синхронизируйте свой hwclock к системному времени, hwclock --systohc

И Вы вернулись в бизнесе. Для непрерывной синхронизации часов с внешним источником необходимо посмотреть на ntp.

Если требуется протестировать батарею CMOS, самый простой способ сделать так состоит в том, чтобы завершить работу системы, удалить шнур питания, ожидать в течение приблизительно 5 минут (чтобы удалить резервную мощность и позволить конденсаторам высушить), затем запуститься, все создает резервную копию. Если Ваши часы сбросили снова, получите новую батарею. Можно обычно находить корректную батарею в аптеке. Если это - сервер, необходимо связаться поставщиком для замены (как можно скорее).

Я не знаю ни о каком механизме в рамках Linux, который позволил бы Вам контролировать состояние своей батареи CMOS.

31
29.08.2013, 20:55
4 ответа

В Unix большинство редакторов работает путем создания нового временного файла, содержащего отредактированное содержание. Когда отредактированный файл сохранен, исходный файл удален, и временный файл переименован к настоящему имени. (Существуют, конечно, различные гарантии для предотвращения dataloss.) Это - например, стиль, используемый sed или perl при вызове с -i ("оперативный") флаг, который не является действительно "оперативным" вообще. Это нужно было назвать "новым местом со старым названием".

Это работает хорошо, потому что Unix гарантирует (по крайней мере, для локальных файловых систем), что открытый файл продолжает существовать, пока он не закрывается, даже если он "удален", и создается новый файл с тем же именем. (Не случайно, что системный вызов Unix для "удаления" файла на самом деле называют, "удаляют связь".) Так, вообще говоря, если интерпретатор оболочки имеет некоторый открытый исходный файл, и Вы "редактируете" файл, таким образом описанный выше, оболочка не будет даже видеть изменений, так как это все еще имеет исходный открытый файл.

[Примечание: как со всеми основанными на стандартах комментариями, вышеупомянутое подвергается нескольким интерпретациям и существуют различные угловые случаи, такие как NFS. Педанты могут заполнить комментарии за исключениями.]

Конечно, возможно изменить файлы непосредственно; это просто не очень удобно для редактирования целей, потому что, в то время как можно перезаписать данные в файле, Вы не можете удалить или вставить, не смещая весь после данных, которые подразумевали бы довольно большую перезапись. Кроме того, при выполнении того смещения содержание файла будет непредсказуемо и процессы, которые имели открытый файл, пострадает. Для схождения с рук этого (как с системами баз данных, например), Вам нужен сложный набор протоколов модификации и распределенных блокировок; материал, который является далеко за пределами объема типичной утилиты редактирования файла.

Так, если Вы хотите отредактировать файл, в то время как то, что это было обработанным оболочкой, у Вас есть две опции:

  1. Можно добавить в файл. Это должно всегда работать.

  2. Можно перезаписать файл с новым содержанием точно той же длины. Это может или не может работать, в зависимости от того, считала ли оболочка уже ту часть файла или нет. Так как большая часть файлового ввода-вывода включает буферы чтения, и так как все оболочки, которые я знаю, читают всю составную команду прежде, чем выполнить его, довольно маловероятно, что можно сойти с рук это. Это, конечно, не было бы надежно.

Я не знаю ни о какой формулировке в стандарте Posix, который на самом деле требует возможности добавления в файл сценария, в то время как файл выполняется, таким образом, это не могло бы работать с каждым Posix совместимая оболочка, а тем более с текущим предложением почти - и оболочки sometimes-posix-compliant. Так YMMV. Но насколько я знаю, это действительно работает надежно с ударом.

Как доказательство, вот реализация "без циклов" печально известных 99 бутылок программы пива в ударе, который использует dd перезаписывать и добавлять (перезапись, по-видимому, безопасна, потому что она заменяет в настоящее время строкой выполнения, которая всегда является последней строкой файла с комментарием точно той же длины; я сделал это так, чтобы конечный результат мог быть выполнен без поведения самоизменения.)

#!/bin/bash
if [[ $1 == reset ]]; then
  printf "%s\n%-16s#\n" '####' 'next ${1:-99}' |
  dd if=/dev/stdin of=$0 seek=$(grep -bom1 ^#### $0 | cut -f1 -d:) bs=1 2>/dev/null
  exit
fi

step() {
  s=s
  one=one
  case $beer in
    2) beer=1; unset s;;
    1) beer="No more"; one=it;;
    "No more") beer=99; return 1;;
    *) ((--beer));;
  esac
}
next() {
  step ${beer:=$(($1+1))}
  refrain |
  dd if=/dev/stdin of=$0 seek=$(grep -bom1 ^next\  $0 | cut -f1 -d:) bs=1 conv=notrunc 2>/dev/null
}
refrain() {
  printf "%-17s\n" "# $beer bottles"
  echo echo ${beer:-No more} bottle$s of beer on the wall, ${beer:-No more} bottle$s of beer.
  if step; then
    echo echo Take $one down, pass it around, $beer bottle$s of beer on the wall.
    echo echo
    echo next abcdefghijkl
  else
    echo echo Go to the store, buy some more, $beer bottle$s of beer on the wall.
  fi
}
####
next ${1:-99}   #
22
27.01.2020, 19:38
  • 1
    , Когда я выполняю это, это не запускается с "Больше", затем продолжается к-1 и в отрицательные числа неограниченно долго. –  Daniel Hershcovich 04.09.2013, 09:50
  • 2
    , Если я сделаю export beer=100 прежде, чем запустить скрипт, это работает как ожидалось. –  Daniel Hershcovich 04.09.2013, 10:10
  • 3
    @DanielHershcovich: совершенно верно; неаккуратное тестирование с моей стороны. Я думаю, что зафиксировал его; это теперь берет дополнительный параметр количества. Лучшая и более интересная фиксация должна была бы автоматически сбросить, если параметр не соответствует кэшируемой копии. –  rici 04.09.2013, 19:14

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

Например, в:

cmd1
cmd2

Оболочка будет читать сценарий блоками, так вероятно, чтение обе команды, интерпретировать первую и затем искать назад в конец cmd1 в сценарии и читают сценарий снова для чтения cmd2 и выполните его.

Можно легко проверить его:

$ cat a
echo foo | dd 2> /dev/null bs=1 seek=50 of=a
echo bar
$ bash a
foo

(хотя смотря на strace вывод на этом, кажется, что это делает некоторые более необычные вещи (как считанный данные несколько раз, ищите назад...) чем тогда, когда я попробовал те же несколько лет назад, таким образом, мой оператор выше о lseeking назад не может больше применяться на более новые версии).

Если однако Вы пишете свой сценарий как:

{
  cmd1
  cmd2
  exit
}

Оболочка должна будет читать до закрытия }, хранилище, что в памяти и выполняют его. Из-за exit, оболочка не будет читать из сценария снова, таким образом, можно будет отредактировать его безопасно, в то время как оболочка интерпретирует его.

С другой стороны, при редактировании сценария, удостоверьтесь, что Вы пишете новую копию сценария. Оболочка будет продолжать читать исходную (даже если это будет удалено или переименовано).

Чтобы сделать это, переименовать the-script кому: the-script.old и копия the-script.old кому: the-script и отредактируйте его.

19
27.01.2020, 19:38

Нет действительно никакого безопасного способа изменить сценарий, в то время как он работает, потому что оболочка может использовать буферизацию для чтения файла. Кроме того, если сценарий будет изменен путем замены его с новым файлом, то оболочки обычно только считают новый файл после выполнения определенных операций.

Часто, когда сценарий изменяется при выполнении, оболочка заканчивает тем, что сообщила о синтаксических ошибках. Это - то, вследствие того, что, когда оболочка закрывает и вновь открыла файл сценария, она использует байтовое смещение в файл, чтобы изменить местоположение себя по возврату.

5
27.01.2020, 19:38

Вы могли обойти это путем поставления капкана на сценарии и затем использования exec взять новое содержание сценария. Отметьте однако, exec звоните запускает сценарий с нуля а не от того, где он достиг в рабочем процессе, и таким образом, сценарий B назовут (на поэтому дальше).

#! /bin/bash

CMD="$0"
ARGS=("$@")

trap reexec 1

reexec() {
    exec "$CMD" "${ARGS[@]}"
}

while : ; do sleep 1 ; clear ; date ; done

Это продолжит отображать дату на экране. Я мог затем отредактировать свой сценарий и изменение date кому: echo "Date: $(date)". При записи, что под управлением сценарий все еще просто отображает дату. Как когда-либо, если я отправляю сигнал, что я установил trap для получения сценарий будет exec (заменяет текущий рабочий процесс указанной командой), который является командой $CMD и аргументы $@. Можно сделать это путем издания kill -1 PID - где PID является PID под управлением сценария - и вывод изменяется на шоу Date: перед date команда производится.

Вы могли сохранить "состояние" своего сценария во внешнем файле (в, говорят, что/tmp), и читают содержание для знания, где "возобновиться" на, когда программа является re-exec'ed. Вы могли затем добавить дополнительное завершение прерываний (SIGINT/SIGQUIT/SIGKILL/SIGTERM) для разрешения этого tmp файл поэтому, когда Вы перезапустите после прерывания "Сценария A", это запустится с начала. Версия с сохранением информации была бы чем-то как:

#! /bin/bash

trap reexec 1
trap cleanup 2 3 9 15

CMD="$0"
ARGS=("$@")
statefile='/tmp/scriptA.state'
EXIT=1

reexec() { echo "Restarting..." ; exec "$CMD" "${ARGS[@]}"; }
cleanup() { rm -f $statefile; exit $EXIT; }
run_scriptB() { /path/to/scriptB; echo "scriptC" > $statefile; }
run_scriptC() { /path/to/scriptC; echo "stop" > $statefile;  }

while [ "$state" != "stop" ] ; do

    if [ -f "$statefile" ] ; then
        state="$(cat "$statefile")"
    else
        state='starting'
    fi

    case "$state" in
        starting)         
            run_scriptB
        ;;
        scriptC)
            run_scriptC
        ;;
    esac
done

EXIT=0
cleanup
4
27.01.2020, 19:38
  • 1
    я устранил ту проблему путем получения $0 и $@ в начале сценария и использующий те переменные в exec вместо этого. –  Drav Sloan 29.08.2013, 03:46

Теги

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