Чем отличается перенаправление файлов bash на стандартный от оболочки (`sh`) в Linux?

Для всех вас, ребята, я нашел обходной путь, который работает для меня .. Может быть, это может вам помочь .. Вот оно:

Raid mount исчезает, потому что система не читает файл mdadm.conf во время загрузки или запуска. Итак, я отредактировал файл /etc/rc.d/rc.local , включив в него следующие команды:

sleep 10
mdadm --assemble --scan
sleep 10
mount -a

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

9
13.04.2017, 21:13
2 ответа

Похожий скрипт, без 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|...

Что означает, что filehandle находится в конце файла, и 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() in input.c ) Выполнение полного системного вызова для каждого байта в принципе не очень эффективно. На практике я не думаю, что чтение будет большим накладным расходом по сравнению, например, с тем фактом, что большинство вещей, которые делает оболочка, включают в себя порождение совершенно новых процессов.

Аналогичная ситуация такова:

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

Подоболочка должна убедиться, что read считывает только первую новую строку, так что head видит следующую строку. (Это также работает с dash.)


Другими словами, Bash делает все возможное, чтобы поддерживать чтение одного и того же источника для самого скрипта и для команд, выполняемых из него. dash нет. zsh и ksh93, упакованные в Debian, идут с Bash в этом вопросе.

12
27.01.2020, 20:04

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

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

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

Bash ищет конец исходного кода команды. код в сценарии перед выполнением команды. Это не то, на что вы можете рассчитывать, не только потому, что другие оболочки этого не делают, но и потому, что это работает, только если оболочка читает из обычного файла. Если оболочка читает из канала (например, ssh remote-host.example.com ), прочитанные данные читаются и не могут быть непрочитаны.

Если вы хотите передать ввод команде в скрипте, вам нужно сделать это явно, как правило, с помощью здесь. (Документ here обычно наиболее удобен для многострочного ввода, но подойдет любой метод.) Написанный вами код работает только в нескольких оболочках, только если скрипт передается в качестве входных данных оболочке из обычного файла; если вы ожидали, что второй whoami будет передан в качестве входных данных для sudo …, подумайте еще раз, имея в виду, что большую часть времени сценарий не передается на стандартный ввод оболочки.

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

Обратите внимание, что в этом десятилетии вы можете использовать sudo -i root. Запуск sudo su — это хак из прошлого.

12
27.01.2020, 20:04

Теги

Похожие вопросы