Могу ли я изменить файл сценария bash (.sh), пока он выполняется?

Вопрос или проблема

Предположим, у меня есть скрипт script.sh, выполнение которого занимает некоторое время. Я запускаю его с помощью ./script.sh. Пока он выполняется в окне терминала, я изменяю файл script.sh. Будет ли это как-то влиять на уже запущенный процесс?

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

Обновление:
Благодарю зарегестрированного пользователя за указание на лучший ответ на Unix.stackexchange.com.

В зависимости от размера скрипта и компилятора/интерпретатора, о которых идет речь, скрипт загружается частично/полностью. Таким образом, если скрипт не загружен полностью, изменения, которые вы вносите в свой скрипт, отразятся на работающем экземпляре, как только часть скрипта будет загружена в память.

Поэтому не рекомендуется изменять ваш скрипт на диске, который в данный момент выполняется, чтобы избежать непредсказуемых результатов: сначала остановите работающий экземпляр, затем измените свой скрипт и снова выполните его.

Начальный ответ: Когда вы вносите изменения в свой скрипт, вы делаете изменения на диске (жесткий диск – постоянное хранилище); когда вы выполняете скрипт, скрипт загружается в вашу память (ОЗУ).

Таким образом, изменения, которые вы вносите в скрипт, не повлияют на выполняющийся скрипт, он будет запускать версию, которую вы выполнили до внесения этих изменений.

Однако, когда вы снова выполняете измененный скрипт, не завершая ранее запущенный экземпляр, будут два экземпляра скрипта – один из которых содержит изменения, а другой – старую версию.

Будьте осторожны, так как ресурсы, которые использует и изменяет скрипт, будут конфликтовать. Например, если вы изменяете файл с помощью скрипта, скрипт, который выполняется позже, не сможет открыть этот файл для записи и не сможет правильно выполниться.

это требует обновления, приведенные выше ответы теперь частично правильны:

с текущей версией bash, изменение скрипта на диске во время его выполнения приведет к тому, что bash “попытается” загрузить изменения в память и принять их во выполняющемся скрипте. если ваши изменения идут после текущей выполняемой строки, новые строки будут загружены и выполнены. но, это предположение bash, и оно может быть верным или ошибочной.

лучший способ сделать это таков:
1) загрузите скрипт в память
2) удалите скрипт с диска
3) запишите новый скрипт на диск
удалив версию на диске сначала, версия в памяти теряет свои связи с ней, так что, когда вы предоставляете новую версию на шаге 3, bash не пытается загрузить новое содержимое в версию в памяти.

Я добавлю кое-что, о чем, по-моему, не упоминалось в других ответах. Многое зависит от того, как именно вы редактируете файл. Выполнение echo "stuff" >file через оболочку (другой экземпляр) действительно перезапишет файл, как я думаю. Однако если вы редактируете файл, например, с помощью emacs, а затем сохраняете, этого не произойдет. В этом случае редактор переименовывает старый файл, чтобы сделать его резервной копией (возможно, удаляя предыдущую резервную копию), и затем записывает измененное содержимое буфера в виде нового файла с (теперь освобожденным) старым именем. Поскольку оболочка (или другой интерпретатор), читающий скрипт, почти наверняка откроет файл только один раз, он затем независим от местоположения имени файла, просто продолжает читать физический файл на диске (идентифицируемый по номеру индекса), который был связан с именем файла во время его открытия. Так что даже если он считывает скрипт блоками (что было бы самым простым решением при использовании буферизованного текстового ввода-вывода), он продолжит читать строки из старого экземпляра файла, которые, вероятно, не изменятся при редактировании.

Ответ @jobin в целом верен, но я добавлю другие ответы, которые могут быть уместными в зависимости от того, что вы хотите сделать.

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

Если вы хотите изменить поведение скрипта на лету, у вас более сложная проблема. Я ожидаю, что вам потребуется встроить это в код скрипта. Bash-скрипты могут обрабатывать сигналы (например, могут ловить такие как kill -USR1 [pid]), и скрипт может отреагировать, перезагрузив некоторый код. Так что, возможно, вы можете получить функциональность, близкую к тому, что вы хотите, но не зная, что именно вы пытаетесь достичь, я не вижу хорошей причины для этого, и предполагаю, что если вы хотите сделать что-то настолько сложное, вам, вероятно, понадобится более сложный язык программирования для этого.

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

Оболочка не похожа на другие языки скриптования.

Оболочка читает и выполняет команды одну за другой, так что простой ответ на ваш вопрос “да, редактирование выполняющегося скрипта может повлиять на него”, но важно понимать детали.

Если вы осторожны, вы можете добавлять строки в конец выполняющегося скрипта, и они будут проигрываться после других команд, уже присутствующих в скрипте.

echo "echo 'Эта дополнительная команда также выполнится.'" >> yourscript.sh

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

Но пока вы все еще имеете дело с тем же индексом, работающая оболочка будет затронута, если скрипт изменится после точки, до которой она дочитала – разве что, конечно, вызван exit до этого.

Чтение обычно выполняется блоками по 4 КиБ, так что небольшие скрипты могут казаться невлиятельными, но когда дело доходит почти до конца файла, оболочка (как и все текстовые программы) будет читать все, что можно. Это “короткое чтение” само по себе не сигнализирует о конце файла, и оболочка будет продолжать читать, пока не прочитает ноль байтов вообще. Таким образом, если файл становится длиннее, когда оболочка его читает, оболочка будет читать новые данные.

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

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

Редактирование файла и вставка N байтов в середину передвигает последние N байтов файла чуть дальше точки, где раньше был конец файла. Эти байты будут считаны как “новые” оболочкой, даже если она только что их читала в их предыдущем месте.

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

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

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

Просто заключите все команды в {}, таким образом:

#!/bin/bash

shopt -s extglob
{

# весь ваш скрипт здесь, включая определения функций

exit    # добавьте код выхода для этой линии, если хотите
}

Опция extglob изменяет работу анализатора, так что не будет работать, если она находится внутри блока, который анализируется, или даже на одной строке с открывающей скобкой.
Если вы хотите использовать её, она должна быть не менее чем на одну строку перед открывающей скобкой, и не связана с ней с помощью исходящей | или || или &&, или не заключенная с ней в составное утверждение большего размера.

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

Модификация файла во время его анализа все еще будет вмешиваться, но это редко занимает больше одной секунды.

Спасибо за ответы.

Просто чтобы убедиться, что я правильно понимаю: когда оболочка выполняет инструкцию на строке номер N скрипта, ссылка идет на инструкцию, а не на номер строки. Когда оболочка заканчивает выполнение инструкции, она ищет инструкцию в скрипте и выполняет следующую инструкцию вместо выполнения инструкции на строке N+1.

Так что если я только добавлю строки перед выполняющейся инструкцией, это не повлияет на выполняющийся скрипт.

Это верно?

Если да, то что будет, если я изменю строку, которую скрипт в данный момент выполняет? Если я изменю параметр в инструкции, сможет ли оболочка идентифицировать ее как инструкцию, которую она выполняла?

.

Ответ или решение

Вопрос о том, можно ли изменить bash-скрипт (.sh) во время его выполнения, требует внимательного рассмотрения, так как взаимодействие между дисковыми операциями и памятью в операционных системах, таких как Unix и Linux, может быть несколько сложным.

Теория

Когда bash-скрипт запускается, оболочка (например, bash) считывает команды из файла последовательно. Это означает, что во время выполнения скрипт не загружается в память целиком в виде единого блока. Вместо этого, оболочка считывает команды по мере необходимости, что позволяет вносить изменения в скрипт. Однако результаты этих изменений могут варьироваться в зависимости от точной природы изменений и того, на каком этапе выполнения находится скрипт.

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

Однако, если изменения вносятся непосредственно в файл (например, с помощью команды echo "новая строка" >> script.sh), то идущая инстанция скрипта может учитывать эти изменения, особенно если они добавляют контент после уже обработанных частей файла.

Пример

Предположим, у вас есть скрипт script.sh, который выполняет некоторую длительную операцию. Вы запускаете скрипт с командой ./script.sh. Пока он выполняется, вы решаете внести изменения в код скрипта. Если вы добавляете новые строки в конец файла, либо модифицируете строки, которые ещё не были считаны оболочкой, изменения могут быть учтены выполняющимся скриптом.

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

Применение

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

  1. Остановите выполняющийся скрипт.
  2. Внесите необходимые изменения в код.
  3. Перезапустите скрипт.

Если изменения необходимы и не могут быть отложены, и вы уверены, что они не повлияют на уже выполненные части скрипта (например, они касаются только последующих операций), существует метод, как правильно и безопасно внести изменения:

  1. Открыть файл для изменения и удалить оригинальную версию скрипта.
  2. Сохранить изменения под тем же именем файла.
  3. Это гарантирует, что выполняющийся процесс не увидит новый контент до завершения своего цикла.

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

Кроме того, для сложных изменений вы можете использовать механизмы обработки сигналов (например, kill -USR1 [pid]), что может быть встроено в код скрипта для его динамического обновления, но это требует изначального проектирования скрипта с учетом таких возможностей.

Таким образом, ответ на вопрос о возможности изменения скрипта во время выполнения зависит от точных условий и требований вашей задачи. В большинстве случаев, рекомендуется осторожность и взвешенность действий, чтобы избежать непредвиденных ошибок или конфликтов.

Оцените материал
Добавить комментарий

Капча загружается...