Чим перенаправлення файлу bash на стандартне відрізняється від оболонки (`sh`) в Linux?


9

Я написав скрипт, який перемикає користувачів під час роботи, і виконує його, використовуючи перенаправлення файлів на стандартне дюйма. Так user-switch.shце ...

#!/bin/bash

whoami
sudo su -l root
whoami

І керування ним bashдає мені поведінку, яку я очікую

$ bash < user-switch.sh
vagrant
root

Однак якщо я запускаю сценарій sh, я отримую різні результати

$ sh < user-switch.sh 
vagrant
vagrant

Чому bash < user-switch.shдається інший вихід, ніж sh < user-switch.sh?

Примітки:

  • трапляється на двох різних коробках під управлінням Debian Jessie

1
Не відповідь, але щодо sudo su: unix.stackexchange.com/questions/218169/…
Kusalananda

1
є / bin / sh = тире на Debian? Я не можу дорікнути на RHEL де / bin / sh = bash
Jeff Schaller

1
/bin/shна (моя копія) Debian є Dash, так. Я навіть до цього часу навіть не знав, що це було. Я до цих пір застряг з башем.
popedotninja

1
Баш поведінка не гарантують роботу будь-якої документально семантиці небудь.
Чарльз Даффі

Хтось сьогодні вказав мені, що сценарій, який я виконував, порушив семантику того, як баш планується використовувати. На це питання було кілька чудових відповідей, але варто зазначити, що, мабуть, не слід перемикати середній сценарій користувачів. Спочатку я спробував user-switch.shу роботі Дженкінса, і це сценарій виконаний. Запуск одного сценарію поза Дженкінсом дав різні результати, і я вдався до перенаправлення файлів, щоб отримати поведінку, яку я бачив у Дженкінсі.
popedotninja

Відповіді:


12

Аналогічний сценарій, без sudo, але подібних результатів:

$ cat script.sh
#!/bin/bash
sed -e 's/^/--/'
whoami

$ bash < script.sh
--whoami

$ dash < script.sh
itvirta

З bash, інша частина сценарію йде в якості вхідних даних sed, з dash, в тлумачить її оболонки.

Запуск straceцих: dashчитає блок скрипту (тут вісім кБ, більше, ніж вимагає утримувати весь сценарій), а потім породжує sed:

read(0, "#!/bin/bash\nsed -e 's/^/--/'\nwho"..., 8192) = 36
stat("/bin/sed", {st_mode=S_IFREG|0755, st_size=73416, ...}) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|...

Що означає, що файл файлу знаходиться в кінці файлу, і sedвін не побачить жодного вводу. Інша частина буферується всередині dash. (Якщо сценарій був більшим за розмір блоку 8 кБ, решта частина буде прочитана sed.)

Bash, з іншого боку, намагається повернутися до кінця останньої команди:

read(0, "#!/bin/bash\nsed -e 's/^/--/'\nwho"..., 36) = 36
stat("/bin/sed", {st_mode=S_IFREG|0755, st_size=73416, ...}) = 0
...
lseek(0, -7, SEEK_CUR)                  = 29
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|...

Якщо вхід надходить з труби, як тут:

$ cat script.sh | bash

перемотування не можна проводити, оскільки труби та розетки не можна знайти. У цьому випадку Bash відновлює читання вводу по одному символу за раз, щоб уникнути його перечитання. ( fd_to_buffered_stream()вinput.c ) Здійснення повного системного виклику для кожного байта в принципі не дуже ефективно. На практиці я не вважаю, що зчитування буде великим надмірним порівнянням, наприклад, з тим, що більшість речей оболонки передбачає нерест цілих нових процесів.

Схожа ситуація така:

echo -e 'foo\nbar\ndoo' | bash -c 'read a; head -1'

Підрозділ повинен переконатися, що readвін читає лише перший новий рядок, щоб headпобачити наступний рядок. (Це dashтеж працює .)


Іншими словами, Bash іде на додаткові тривалості, щоб підтримувати читання того самого джерела як для самого сценарію, так і для команд, що виконуються з нього. dashне робить. І zsh, і ksh93упакований у Debian, йде разом із Bash.


1
Чесно кажучи, я трохи здивований, що це працює.
ilkkachu

дякую за чудову відповідь! Я запускаю обидві команди, використовуючи straceпізніше та порівняю результати. Я не хотів би використовувати такий підхід.
popedotninja

2
Так, моя реакція на читання питання було здивування , що робить роботу з Баш - у мене було підшиті як то , що , безумовно , не працюватиме. Я здогадуюсь, що я був лише наполовину правильний :)
заварювання

12

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

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

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

Bash прагне до кінця вихідного коду команди в скрипті перед виконанням команди. На це можна не розраховувати не лише тому, що інші оболонки цього не роблять, а й тому, що це працює лише в тому випадку, якщо оболонка зчитується із звичайного файлу. Якщо оболонка ssh remote-host.example.com <local-script-file.shзчитується з труби (наприклад ), прочитані дані читаються і не можуть бути непрочитаними.

Якщо ви хочете передати вхід команді в сценарії, вам потрібно зробити це явно, як правило, з документом тут . (Тут документ, як правило, найзручніший для багаторядкового введення, але будь-який метод буде робити.) Код, який ви написали, працює лише в декількох оболонках, лише якщо сценарій передається як вхід до оболонки з звичайного файлу; якщо ви очікували, що другий whoamiбуде переданий як вхід sudo …, подумайте ще раз, пам’ятаючи, що більшість часу сценарій не передається стандартному вводу оболонки.

#!/bin/bash
whoami
sudo su -l root <<'EOF'
whoami
EOF

Зауважте, що це десятиліття ви можете використовувати sudo -i root. Біг sudo su- хак з минулого.

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