Як Linux має справу зі скриптами оболонки?


22

Для цього питання розглянемо сценарій bash shell, хоча це питання має бути застосовано до всіх типів скрипту оболонки.

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

Іншими словами, якщо я виконую скрипт оболонки і видаляю його до завершення виконання, чи буде виконання припинено, чи продовжиться так, як є?


3
Спробуй це. (Продовжиться.)
девнул

1
@devnull тут насправді цікаве питання. Зрозуміло, продовжуватиметься це чи ні, це нетривіально для перевірки, але існують відмінності між бінарними файлами (які завантажуються в пам'ять) та сценаріями з рядком shebang або сценаріями без рядка shebang.
terdon

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)                           = ?

У вищесказаному ми можемо чітко бачити, що весь сценарій, як видається, читається як одне ціле, а потім виконується там після. Так що, принаймні у випадку Баша, "з'явиться" , що він читає файл, а потім виконує його. Тож ви можете подумати, що ви можете редагувати сценарій під час його запуску?

ПРИМІТКА: Хоча не варто! Читайте далі, щоб зрозуміти, чому вам не слід возитися із запущеним файлом сценарію.

Що з іншими перекладачами?

Але ваше запитання трохи не стоїть. Це не обов'язково завантаження вмісту файлу в Linux, це інтерпретатор, який завантажує вміст, так що це дійсно залежить від того, як реалізований інтерпретатор, чи завантажує він файл цілком, або в блоки або рядки одночасно.

То чому ми не можемо редагувати файл?

Якщо ви використовуєте набагато більший сценарій, то помітите, що вищевказаний тест трохи вводить в оману. Насправді більшість перекладачів завантажують свої файли в блоки. Це досить стандартно для багатьох інструментів Unix, де вони завантажують блоки файлу, обробляють його та завантажують ще один блок. Ви можете бачити таку поведінку в цьому питаннях U&L Q&A, про які я писав деякий час тому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 та інші оболонки, ймовірно, не завантажуватимуть файл у повному обсязі, скоріше вони читають їх у блоках.

Список літератури


@terdon - так, я пам’ятаю, як бачив цю запитання і раніше.
slm

5
З 40-байтним сценарієм, безумовно, він читається в одному блоці. Спробуйте зі сценарієм> 8 КБ.
Жиль "ТАК - перестань бути злим"

Я ніколи не намагався, але думаю, що видалення файлів насправді не робиться, поки всі процеси не закриють дескриптор файлу, пов'язаний із видаленим файлом, тому bash може продовжувати читати з видаленого файлу.
Фарид Нурі Нешат

@Gilles - так, я додав приклад, добирався до нього.
slm

2
Така поведінка залежить від версії. Я перевірив bash версії 3.2.51 (1) -release і виявив, що він не буферизується через поточний рядок (див. Цю відповідь stackoverflow ).
Гордон Девіссон

11

Це більше оболонки, ніж залежно від ОС.

Залежно від версії, kshпрочитайте сценарій на вимогу блоком 8k або 64k байтів.

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

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

Якщо використовуються структуровані команди оболонки ( випадок, коли прийнята відповідь не розглядається ), як for/do/doneцикл, acase/esac комутатор, документ тут, підзаглушка, додана в дужки, визначення функції тощо, та будь-яка комбінація перерахованого, інтерпретатори оболонки зчитуються до кінця конструкції, щоб спочатку переконатися у відсутності синтаксичної помилки.

Це дещо неефективно, оскільки один і той же код можна читати знову і знову велику кількість разів, але пом'якшується тим, що цей вміст зазвичай кешується.

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

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


7

Це залежить від того, як працює інтерпретатор, на якому працює сценарій. Все, що ядро ​​робить, це помітити файл, який потрібно виконати, починається з#! , по суті, запускає решту рядка як програма і надає йому виконуваний файл як аргумент. Якщо перерахований там перекладач читає цей файл рядок за рядком (як це роблять інтерактивні оболонки з тим, що ви вводите), саме це ви отримуєте (але багаторядкові структури циклу читаються і зберігаються навколо для повторення); якщо інтерпретатор забиває файл у пам'ять, обробляє його (можливо, компілює його в проміжне представлення, як, наприклад, Perl та Pyton do), файл читається повністю перед виконанням.

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


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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.