Вот еще один способ сортировки вывода без использования внешних двоичных файлов, таких как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.
Существует разница в том, как 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.
Вместо этого рассмотрим этот пример:
$ 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