Обычно, если вы редактируете scrpit, все запущенные сценарии подвержены ошибкам.
Насколько я понимаю, bash (другие оболочки тоже?) Считывает скрипт постепенно, поэтому, если вы изменили файл скрипта извне, он начинает читать неправильные вещи. Есть ли способ предотвратить это?
Пример:
sleep 20
echo test
Если вы выполните этот скрипт, bash прочитает первую строку (скажем, 10 байт) и перейдет в спящий режим. Когда он возобновляется, в скрипте может быть другое содержимое, начиная с 10-го байта. Я могу быть в середине строки в новом сценарии. Таким образом, запущенный скрипт будет сломан.
\n
позволил бы? Может подделать()
подойдет? Я не очень опытен с этим, пожалуйста, помогите!sleep 20 ;\n echo test ;\n sleep 20
я, и я начинаю его редактировать, он может плохо себя вести. Например, bash может прочитать первые 10 байтов скрипта, понятьsleep
команду и перейти в спящий режим. После возобновления в файле будет другое содержимое, начиная с 10 байтов.Ответы:
Да, оболочки,
bash
в частности, внимательно читают файл по одной строке за раз, поэтому он работает так же, как при интерактивном использовании.Вы заметите, что когда файл не доступен для поиска (например, канал),
bash
даже читает по одному байту за раз, чтобы не читать после\n
символа. Когда файл доступен для поиска, он оптимизирует, считывая полные блоки за раз, но возвращаясь к после\n
.Это означает, что вы можете делать такие вещи, как:
Или написать сценарии, которые обновляются сами. Что бы вы не смогли сделать, если бы это не дало вам такой гарантии.
Сейчас редко можно делать подобные вещи, и, как вы узнали, эта функция имеет тенденцию мешать чаще, чем полезна.
Чтобы избежать этого, вы можете попробовать и убедиться , что вы не измените файл на месте (например, изменить копию и переместите копию на месте (например ,
sed -i
илиperl -pi
и некоторые редакторы делают, например)).Или вы можете написать свой сценарий как:
(обратите внимание, что важно, чтобы он
exit
находился на одной линии с}
; хотя вы могли бы также поместить его в фигурные скобки непосредственно перед закрывающей).или:
Оболочке нужно будет прочитать скрипт до тех пор, пока он
exit
не начнет что-либо делать. Это гарантирует, что оболочка не будет читать из сценария снова.Это означает, что весь скрипт будет храниться в памяти.
Это также может повлиять на синтаксический анализ сценария.
Например, в
bash
:Выведет, что U + 00E9 закодировано в UTF-8. Однако, если вы измените его на:
\ue9
Будет расширены в кодировке , которая была в действии в то время, когда команда была разобрана , который в этом случае , прежде чемexport
команда будет выполнена.Также обратите внимание, что если команда
source
aka.
используется с некоторыми оболочками, у вас возникнет та же проблема с исходными файлами.Это не тот случай,
bash
когда чьяsource
команда полностью читает файл перед его интерпретацией. Если писатьbash
специально, вы можете использовать это, добавив в начале сценария:(Я бы не стал полагаться на это, хотя, как вы могли себе представить, будущие версии
bash
могут изменить это поведение, которое в настоящее время можно рассматривать как ограничение (bash и AT & T ksh - единственные POSIX-подобные оболочки, которые, насколько можно судить, ведут себя так же) иalready_sourced
хитрость немного хрупкая, так как она предполагает, что переменная находится вне среды, не говоря уже о том, что она влияет на содержимое переменной BASH_SOURCE)источник
}; exec true
. Таким образом, нет необходимости в новых строках в конце файла, что удобно для некоторых редакторов (например, emacs). Все тесты, с которыми я мог правильно работать,}; exec true
}; exit
? Вы также теряете статус выхода.. script
).Вам просто нужно удалить файл (т.е. скопировать его, удалить его, переименовать копию обратно к исходному имени). На самом деле многие редакторы могут быть настроены для вас. Когда вы редактируете файл и сохраняете в нем измененный буфер, вместо перезаписи файла он переименует старый файл, создаст новый и поместит новое содержимое в новый файл. Следовательно, любой запущенный скрипт должен продолжаться без проблем.
Используя простую систему контроля версий, такую как RCS, которая легко доступна для vim и emacs, вы получаете двойное преимущество - наличие истории ваших изменений, и система извлечения должна по умолчанию удалить текущий файл и воссоздать его с правильными режимами. (Остерегайтесь жестких ссылок на такие файлы, конечно).
источник
Самое простое решение:
Таким образом, Bash будет читать все
{}
блок перед его выполнением, аexit
директива будет следить за тем, чтобы ничего не читалось за пределами блока кода.Если вы не хотите «исполнять» скрипт, а скорее «исходить» из него, вам нужно другое решение. Это должно работать тогда:
Или, если вы хотите прямой контроль над кодом выхода:
Вуаля! Этот скрипт безопасен для редактирования, создания и исполнения. Вы все еще должны быть уверены, что не измените его в те миллисекунды, когда он изначально читается.
источник
Доказательство концепции. Вот скрипт, который модифицирует себя:
мы видим измененную версию печати
Это связано с тем, что bash load сохраняет дескриптор файла для открытия в сценарии, поэтому изменения в файле будут видны сразу.
Если вы не хотите обновлять копию в памяти, отсоедините исходный файл и замените его.
Один из способов сделать это - использовать sed -i.
доказательство концепции
Если вы используете редактор для изменения скрипта, включение функции «сохранить резервную копию» может быть всем, что необходимо, чтобы редактор записал измененную версию в новый файл вместо перезаписи существующей.
источник
bash
не открывает файл сmmap()
. Просто нужно внимательно читать по одной строке за раз, так же, как когда он получает команды от терминального устройства в интерактивном режиме.Обертывание вашего скрипта в блок
{}
, вероятно, является лучшим вариантом, но требует изменения ваших скриптов.будет вторым лучшим вариантом (при условии tmpfs ) недостатком является то, что он ломает $ 0, если ваши сценарии используют это.
используя что-то вроде
F=test.sh; tail -n $(cat "$F" | wc -l) "$F" | bash
менее идеально, потому что оно должно держать весь файл в памяти и ломать $ 0.следует избегать прикосновения к исходному файлу, чтобы не нарушать время последнего изменения, блокировки чтения и жесткие ссылки. таким образом, вы можете оставить редактор открытым при запуске файла, и rsync не будет без необходимости проверять контрольную сумму файла на наличие резервных копий и жестких ссылок, как и ожидалось.
замена файла при редактировании работала бы, но менее надежна, поскольку не применяется другими скриптами / пользователями / или кто-то может забыть. И снова это сломало бы жесткие ссылки.
источник
tac test.sh | tac | bash