в bash читайте после того, как канал не устанавливает значения

22

Изменить: оригинальное название было "чтение не удается в Bash"

С помощью ksh я использую read как удобный способ разделения значений:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 
2 1
$

Но это не сработает в bash:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 

$

Я не нашел причину в справочной странице, почему это не помогает, любая идея?

Эммануэль
источник
2
Это обсуждается (несколько непонятно) на странице 024 Greg's Bash FAQ .
Скотт

Ответы:

27

bash запускает правую часть конвейера в контексте подоболочки , поэтому изменения в переменных (что и readпроисходит) не сохраняются - они умирают, когда это делает подоболочка, в конце команды.

Вместо этого вы можете использовать процесс подстановки :

$ read a b dump < <(echo 1 2 3 4 5)
$ echo $b $a
2 1

В этом случае readон работает в нашей основной оболочке, а наша производящая команда запускается в подоболочке. <(...)Синтаксис создает подоболочку и подключает свой выход к трубе, которую мы перенаправлять на вход readс обычной <операцией . Поскольку readзапускается в нашей основной оболочке, переменные установлены правильно.

Как указано в комментарии, если ваша цель буквально как-то разделить строку на переменные, вы можете использовать строку здесь :

read a b dump <<<"1 2 3 4 5"

Я предполагаю, что есть нечто большее, чем это, но это лучший вариант, если нет.

Майкл Гомер
источник
3
Или даже read a b dump <<< '1 2 3 4 5'.
Чороба
Спасибо всем, кстати, я заметил, что mksh (на cygwin) делает то же самое, что и bash.
Эммануэль
@ Майкл Гомер Хорошее объяснение! Я нашел еще один пример , который может объяснить , что каждая команда в трубопроводе работать в собственном субоболочке: cat /etc/passwd | (read -r line ; echo $line). Но следующий echoиз $lineкоторых не находится в конвейере, ничего не выводит на экран, потому что значение существовало только в скобках (подоболочка). Надеюсь, это кому-то поможет.
Юрий Гончарук
17

Это не bashошибка, поскольку POSIXдопускает bashи kshповедение, и поведение, приводящее к печальному расхождению, которое вы наблюдаете.

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_12

Кроме того, каждая команда многокомпонентного конвейера находится в среде подоболочек; однако в качестве расширения любая или все команды в конвейере могут выполняться в текущей среде. Все остальные команды должны выполняться в текущей среде оболочки.

Однако, с bash 4.2и новее, вы можете установить lastpipeопцию в неинтерактивных сценариях, чтобы получить ожидаемый результат, например:

#!/bin/bash

echo 1 2 3 4 5 | read a b dump
echo before: $b $a 
shopt -s lastpipe
echo 1 2 3 4 5 | read a b dump
echo after: $b $a 

Выход:

before:
after: 2 1
jlliagre
источник
1
+1 спасибо за информацию о "последней трубе". извините за задержку
Эммануэль
1
проблема в lastpipeтом, что он не работает в других оболочках (например, в тире). в принципе нет никакого способа сделать этот переносной,
кроме
1
@anarcat Это правильно, но заданный здесь вопрос касался bash.
jlliagre
@anarcat: это может измениться в будущем, так как есть желание изменить POSIX по другой причине (статус PIPE), см. здесь: unix.stackexchange.com/questions/476834/… Изменение других оболочек не тривиально, мне потребовалось несколько месяцев переписать синтаксический анализатор и интерпретатор в Bourne Shell (bosh), чтобы реализовать более быстрое поведение современного ksh.
Щили