в bash, читайте після того, як труба не встановлює значення


22

Редагувати: оригінальна назва була "невдача читання в баші"

За допомогою ksh я використовую read як зручний спосіб розділення значень:

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

Але це не вдається в башті:

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

$

Я не знайшов причину на сторінці man, чому це не вдається, будь-яка ідея?


2
Про це йдеться (дещо незрозуміло) на сторінці 024 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'.
choroba

Дякую всім, btw Я зазначив, що mksh (на cygwin) робить те саме, що і bash.
Еммануель

@Michael Homer Добре пояснення! Я знайшов ще один приклад , який може пояснити , що кожна команда в трубопроводі працювати у власному субоболочке: 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

1
+1 дякую за інформацію про "lastpipe". вибачте за затримку
Еммануель

1
проблема lastpipeполягає в тому, що він не працює в інших оболонках (наприклад, тире). немає в принципі ніякого способу зробити це портативний доходячи працює все в цьому субоболочке см stackoverflow.com/questions/36268479 / ...
anarcat

1
@anarcat Це правильно, але питання, яке тут задалося, стосувалося bash.
jlliagre

@anarcat: Це може змінитися в майбутньому, оскільки є бажання змінити POSIX з іншої причини (статус PIPE), дивіться тут: unix.stackexchange.com/questions/476834/…. Зміна інших оболонок не тривіальна, на це пішло кілька місяців переписати парсер і інтерпетер на оболонці Борна (бош), щоб реалізувати більш швидку поведінку сучасного кш.
schily
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.