Как Linux работает со скриптами оболочки?

22

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

Когда кто-то выполняет сценарий оболочки, Linux загружает все сценарии одновременно (возможно, в память) или читает команды сценария одну за другой (строка за строкой)?

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

зарегистрированный пользователь
источник
3
Попытайся. (Это будет продолжаться.)
devnull
1
@devnull здесь на самом деле интересный вопрос. Конечно, будет ли тестирование продолжаться или нет, но есть различия между двоичными файлами (которые загружаются в память) и сценариями со строкой Шебанга или сценариями без строки Шебанга.
Тердон
1
Вы можете быть заинтересованы в этом ответе
terdon
23
Для вашего реального намерения, для удаления сценария оболочки во время его выполнения, не имеет значения, будет ли он прочитан сразу или построчно. В Unix inode фактически не удаляется (даже если на него нет ссылок из какого-либо каталога) до тех пор, пока не будет закрыт последний открытый файл. Другими словами, даже если ваша оболочка читает в сценарии оболочки построчно во время выполнения, ее все равно можно безопасно удалить. Единственное исключение - если ваша оболочка является той, которая закрывает и снова открывает скрипт оболочки, но если это происходит, у вас возникают гораздо большие проблемы (с безопасностью).
Крис Шестер-Янг

Ответы:

33

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

пример

Скажем, у меня есть этот сценарий оболочки.

$ cat hello_ul.bash 
#!/bin/bash

echo "Hello Unix & Linux!"

Запуск его с помощью strace:

$ strace -s 2000 -o strace.log ./hello_ul.bash
Hello Unix & Linux!
$

Если заглянуть внутрь strace.logфайла, обнаружится следующее.

...
open("./hello_ul.bash", O_RDONLY)       = 3
ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7fff0b6e3330) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
read(3, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 80) = 40
lseek(3, 0, SEEK_SET)                   = 0
getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
fcntl(255, F_GETFD)                     = -1 EBADF (Bad file descriptor)
dup2(3, 255)                            = 255
close(3)     
...

Как только файл прочитан, он выполняется:

...
read(255, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 40) = 40
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc0b38ba000
write(1, "Hello Unix & Linux!\n", 20)   = 20
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(255, "", 40)                       = 0
exit_group(0)                           = ?

Выше мы ясно видим, что весь сценарий читается как единая сущность, а затем выполняется там. Таким образом, он «появился бы», по крайней мере, в случае Bash, в котором он читает файл, а затем выполняет его. Таким образом, вы думаете, что можете редактировать сценарий, пока он работает?

ПРИМЕЧАНИЕ: не надо, хотя! Читайте дальше, чтобы понять, почему вы не должны связываться с запущенным файлом сценария.

А как насчет других переводчиков?

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

Так почему же мы не можем отредактировать файл?

Если вы используете гораздо больший скрипт, вы заметите, что приведенный выше тест немного вводит в заблуждение. Фактически большинство интерпретаторов загружают свои файлы в блоки. Это довольно стандартно для многих инструментов Unix, где они загружают блоки файла, обрабатывают его, а затем загружают другой блок. Вы можете увидеть это поведение с этими вопросами и ответами U & L, которые я написал некоторое время назад относительно grep: « Сколько текста использует grep / egrep каждый раз? ,

пример

Скажем, мы делаем следующий скрипт оболочки.

$ ( 
    echo '#!/bin/bash'; 
    for i in {1..100000}; do printf "%s\n" "echo \"$i\""; done 
  ) > ascript.bash;
$ chmod +x ascript.bash

В результате этого файла:

$ ll ascript.bash 
-rwxrwxr-x. 1 saml saml 1288907 Mar 23 18:59 ascript.bash

Который содержит следующий тип контента:

$ head -3 ascript.bash ; echo "..."; tail -3 ascript.bash 
#!/bin/bash
echo "1"
echo "2"
...
echo "99998"
echo "99999"
echo "100000"

Теперь, когда вы запустите это, используя ту же технику выше strace:

$ strace -s 2000 -o strace_ascript.log ./ascript.bash
...    
read(255, "#!/bin/bash\necho \"1\"\necho \"2\"\necho \"3\"\necho \"4\"\necho \"5\"\necho \"6\"\necho \"7\"\necho \"8\"\necho \"9\"\necho \"10\"\necho 
...
...
\"181\"\necho \"182\"\necho \"183\"\necho \"184\"\necho \"185\"\necho \"186\"\necho \"187\"\necho \"188\"\necho \"189\"\necho \"190\"\necho \""..., 8192) = 8192

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

Ссылки

SLM
источник
@terdon - да, я помню, что видел Q & A раньше.
SLM
5
С 40-байтовым скриптом, конечно, он читается в одном блоке. Попробуйте с> 8kB сценарием.
Жиль "ТАК - перестань быть злым"
Я никогда не пытался, но я думаю, что удаление файлов на самом деле не выполняется, пока все процессы не закроют дескриптор файла, связанный с удаленным файлом, поэтому bash может продолжить чтение из удаленного файла.
Фарид Нури Нешат
@ Жиль - да, я добавил пример, добирался до него.
SLM
2
Это поведение зависит от версии. Я протестировал релизную версию bash 3.2.51 (1) и обнаружил, что она не буферизируется после текущей строки (см. Этот ответ stackoverflow ).
Гордон Дэвиссон
11

Это больше зависит от оболочки, чем от ОС.

В зависимости от версии, kshчитайте скрипт по требованию размером 8 или 64 КБ.

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

Это для простых конструкций, то есть набора простых команд.

Если используются структурированные команды оболочки ( случай, когда принятый ответ пропущен ), например, for/do/doneцикл, case/esacпереключатель, документ here, подоболочка, заключенная в скобки, определение функции и т. Д. И любая комбинация вышеперечисленного, интерпретаторы оболочки считывают до конца конструкции сначала убедитесь, что нет синтаксической ошибки.

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

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

Также обратите внимание, что bash может аварийно завершить работу с нарушением сегментации, когда он не может сохранить слишком большую конструкцию сценария, которую ksh93 может прочитать безупречно.

jlliagre
источник
7

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

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

vonbrand
источник
4

Файл 'x':

cat<<'dog' >xyzzy
LANG=C
T=`tty`
( sleep 2 ; ls -l xyzzy >$T ) &
( sleep 4 ; rm -v xyzzy >$T ) &
( sleep 4 ; ls -l xyzzy >$T ) &
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
dog

sh xyzzy

Бег:

~/wrk/tmp$ sh x
alive.
alive.
alive.
-rw-r--r-- 1 yeti yeti 287 Mar 23 16:57 xyzzy
alive.
removed `xyzzy'
ls: cannot access xyzzy: No such file or directory
alive.
alive.
alive.
alive.
~/wrk/tmp$ _

IIRC файл не удаляется, пока процесс сохраняет его открытым. Удаление просто удаляет данный DIRENT.


источник