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

У меня есть общий вопрос, который может быть результатом непонимания процессов обработки в Linux.

В моих целях я собираюсь определить «скрипт» как фрагмент кода bash, сохраненный в текстовом файле с разрешениями на выполнение для текущего пользователя.

  • Выбросы кеша Linux
  • Bash: опрос на создание файла и завершение, если выходы
  • Получить имя эмулятора терминала внутри сценария оболочки
  • Что такое MEAN (на Ubuntu)?
  • Можно ли запускать OOM-killer при принудительной замене?
  • Как автоматически установить на исполняемый бит для определенного файла
  • У меня есть серия сценариев, которые называют друг друга в тандеме. Для простоты я буду называть их сценариями A, B и C. Сценарий A выполняет серию утверждений, а затем приостанавливает, затем выполняет скрипт B, затем он приостанавливается, затем выполняет сценарий C. Другими словами, серия шагов выглядит примерно так:

    Выполнить скрипт A:

    1. Серия заявлений
    2. Пауза
    3. Запустить скрипт B
    4. Пауза
    5. Выполнить скрипт C

    Я знаю по опыту, что, если я запускаю сценарий A до первой паузы, а затем делаю изменения в скрипте B, эти изменения отражаются при выполнении кода, когда я разрешаю его возобновлять. Аналогично, если я редактирую сценарий C, пока сценарий A все еще приостановлен, затем разрешите продолжить его после сохранения изменений, эти изменения отражаются при выполнении кода.

    Вот реальный вопрос, есть ли способ редактировать Script A, пока он все еще работает? Или редактирование невозможно после его запуска?

  • Псевдоним для копирования файла в определенный файл
  • Одиночная машина с двумя дисками
  • Использование встроенного регулярного выражения Bash
  • Что означает \ bi \ b в виде grep?
  • Установка в качестве «корня» в настоящее время не поддерживается - что мне делать?
  • Как я могу однозначно определить, какое устройство находится на USB-узле 6-0: 1.0: порт 2??
  • 4 Solutions collect form web for “Что произойдет, если вы отредактируете скрипт во время выполнения?”

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

    Это хорошо работает, потому что unix уверяет (по крайней мере, для локальных файловых систем), что открытый файл продолжает существовать до его закрытия, даже если он «удален», и создается новый файл с тем же именем. (Не случайно, что системный вызов unix для «удаления» файла на самом деле называется «unlink».) Итак, вообще говоря, если интерпретатор оболочки имеет некоторый исходный файл, и вы «редактируете» файл описанным выше способом , оболочка даже не увидит изменения, так как у нее все еще есть открытый файл.

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

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

    Итак, если вы хотите отредактировать файл во время его обработки оболочкой, у вас есть два варианта:

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

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

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

    В качестве доказательства, здесь есть «без петли» реализация позорных 99 бутылок пивной программы в bash, которая использует 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} # 

    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 на это, кажется, он делает несколько более причудливых вещей (например, несколько раз читал данные, искал назад …), чем когда я пробовал то же самое несколько лет назад, поэтому мое заявление выше о том, больше не может применяться в более новых версиях).

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

     { cmd1 cmd2 exit } 

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

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

    Для этого переименуйте the-script в the-script.old и скопируйте the-script.old в the-script и отредактируйте его.

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

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

    Вы можете обойти это, установив ловушку на свой скрипт, а затем используя 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 .

    Вы можете сохранить «состояние» вашего скрипта во внешнем файле (в say / tmp) и прочитать содержимое, чтобы узнать, где «возобновить», когда программа повторно выполняется. Затем вы можете добавить дополнительное прерывание ловушек (SIGINT / SIGQUIT / SIGKILL / SIGTERM), чтобы очистить этот файл tmp, поэтому при перезапуске после прерывания «Script 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 
    Linux и Unix - лучшая ОС в мире.