Почему родительская оболочка здесь -document не работает для команды sub -в тире, но работает в bash?

Вот еще один способ сортировки вывода без использования внешних двоичных файлов, таких какsort:

awk '{arr[substr($0,0,7)]+=$2}END{a=asorti(arr,sort); for(i=1;i<=a;i++) print sort[i], arr[sort[i]]}' infile.
4
24.08.2019, 22:10
2 ответа

Существует разница в том, как Bash и Dash обрабатывают ввод со стандартного ввода. :При запуске команды(suздесь )Bash заботится о том, чтобы оставить позицию чтения на стандартном вводе сразу после строки, вызвавшей запуск команды, либо считывая побайтно для каналов, либо возвращаясь назад, если ввод из файла. Dash это не волнует, он просто читает полные блоки.

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

Таким образом, с Bash он читает su test\nwhoami\n, выполняет поиск сразу после первой новой строки, затем запускает su, который теперь видит whoami\nна входе.

С Dash он читает su test\nwhoami\n, запускает su, который не видит ввода и завершает работу, а затем Dash запускает whoami.


Вы можете видеть то же самое здесь, с Dash, команда dateвыполняется и readполучает пустой ввод, а с Bash эта строка от readдо x.

$ cat test.sh
read x
date
echo "variable x = '$x'"
$ cat test.sh | bash
variable x = 'date'
$ cat test.sh | dash
Sat Aug 24 12:25:23 EEST 2019
variable x = ''

Если я правильно интерпретирую POSIX-описание sh, требуется осторожное поведение Bash, а более слабый подход Dash не соответствует этому:

STDIN
[...] When the shell is using standard input and it invokes a command that also uses standard input, the shell shall ensure that the standard input file pointer points directly after the command it has read when the command begins execution. It shall not read ahead in such a manner that any characters intended to be read by the invoked command are consumed by the shell

Что бы это ни стоило, оболочка Busybox ведет себя здесь как Dash, все остальные, которые я тестировал, ведут себя так же, как Bash.

8
27.01.2020, 20:45

Вместо этого рассмотрим этот пример:

$ cat f
grep pos /proc/self/fdinfo/0
IFS= read -r var
echo A
echo B
printf '%s\n' "var=$var"
$ bash < f
pos:    29
B
var=echo A
$ dash < f
pos:    85
A
B
var=

Как видите, во время выполнения команды grepпозиция в стандартном вводе находится в конце файла с dashи сразу после новой строки, которая следует за командой grepв bash. ].

Команда echo Aзапускается dash, но в случае bashона подается в качестве входных данных для read.

Произошло следующее: dashпрочитал весь ввод (на самом деле, блок текста ), а bashпрочитал по одной строке перед выполнением команд.

Для этого bashнужно было бы читать по одному байту за раз, чтобы убедиться, что он не читается дальше новой строки, но когда ввод является обычным файлом (, как в случае моего fфайл выше, но и здесь -документы, которые bash реализует как временные файлы, в то время как dashиспользует конвейеры ), bashоптимизирует его, читая по блокам и выполняя поиск до конца строки,который вы можете увидеть с помощью straceв Linux:

$ strace -e read,lseek bash < f
[...]
lseek(0, 0, SEEK_CUR)                   = 0
read(0, "grep pos /proc/self/fdinfo/0\nIFS"..., 85) = 85
lseek(0, -56, SEEK_CUR)                 = 29
pos:    29
[...]

$ strace -e read,lseek dash < f
read(0, "grep pos /proc/self/fdinfo/0\nIFS"..., 8192) = 85
pos:    85
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=12422, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
read(0, "", 1)                          = 0
[...]

Когда stdin является терминальным устройством, каждый read()возвращает строки, отправленные терминалом, поэтому обычно вы видите похожее поведение в bashи dash.

В вашем случае вы могли бы сделать:

sudo dash << 'end-of-script'
su test <<"end"
whoami
end
end-of-script

или выше:

sudo sh -c '
  su test -c whoami
'

или даже лучше:

sudo -u test whoami
9
27.01.2020, 20:45

Теги

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