Я пытаюсь получить bash для обработки данных из stdin, который попадает в систему, но не повезло. Я имею в виду не одну из следующих работ:
echo "hello world" | test=($(< /dev/stdin)); echo test=$test
test=
echo "hello world" | read test; echo test=$test
test=
echo "hello world" | test='cat'; echo test=$test
test=
где я хочу, чтобы результат был test=hello world
. Я пробовал поставить "quotes" вокруг "$test"
который тоже не работает.
Использование
IFS= read var << EOF
$(foo)
EOF
Вы можете обмануть read
для приема из этого канала следующим образом:
echo "hello world" | { read test; echo test=$test; }
или даже написать такую функцию:
read_from_pipe() { read "$@" <&0; }
Но нет смысла - ваши переменные назначения могут не длиться! Конвейер может порождать подоболочку, где среда наследуется по значению, а не по ссылке. Вот почему read
не беспокоится о вводе из трубы - это undefined.
FYI, http://www.etalabs.net/sh_tricks.html - отличная коллекция крутизны, необходимая для борьбы с странностями и несовместимостью снарядов борнов, sh.
test=`echo "hello world" | { read test; echo $test; }`
если вы хотите читать много данных и работать над каждой строкой отдельно, вы можете использовать что-то вроде этого:
cat myFile | while read x ; do echo $x ; done
если вы хотите разбить строки на несколько слов, вместо x можно использовать несколько переменных:
cat myFile | while read x y ; do echo $y $x ; done
в качестве альтернативы:
while read x y ; do echo $y $x ; done < myFile
Но как только вы начнете хотеть делать что-нибудь действительно умное с такого рода вещами, вам лучше пойти на какой-то язык сценариев, например perl, где вы можете попробовать что-то вроде этого:
perl -ane 'print "$F[0]\n"' < myFile
Там довольно крутая кривая обучения с perl (или, я думаю, любой из этих языков), но в долгосрочной перспективе вам будет намного легче, если вы хотите сделать что-либо, кроме самого простого из скриптов. Я бы рекомендовал Perl Cookbook и, конечно же, язык программирования Perl Ларри Уолла и др.
Это еще один вариант
$ read test < <(echo hello world)
$ echo $test
hello world
<(..)
имеет над $(..)
состоит в том, что <(..)
возвращает каждую строку вызывающей стороне, как только выполняемая команда делает ее доступной. $(..)
, однако, ожидает завершения команды и генерации всех ее выходных данных, прежде чем она сделает какие-либо выходные данные доступными для вызывающей стороны.
read
не будет считываться из канала (или, возможно, результат будет потерян, потому что канал создает подоболочку). Однако вы можете использовать строку здесь в Bash:
$ read a b c <<< $(echo 1 2 3)
$ echo $a $b $c
1 2 3
Я не эксперт в Bash, но мне интересно, почему это не было предложено:
stdin=$(cat)
echo "$stdin"
Однострочное доказательство того, что оно работает для меня:
$ fortune | eval 'stdin=$(cat); echo "$stdin"'
bash
4.2 вводит параметр lastpipe
, который позволяет вашему коду работать так, как написано, путем выполнения последней команды в конвейере в текущей оболочке, а не в подоболочке.
shopt -s lastpipe
echo "hello world" | read test; echo test=$test
Синтаксис неявной трубы из команды оболочки в переменную bash составляет
var=$(command)
или
var=`command`
В ваших примерах вы передаете данные в оператор присваивания, который не ожидает ввода.
В моих глазах лучший способ читать с stdin в bash - это следующий, который также позволяет вам работать над строками до окончания ввода:
while read LINE; do
echo $LINE
done < /dev/stdin
Первая попытка была довольно близкой. Этот вариант должен работать:
echo "hello world" | { test=$(< /dev/stdin); echo "test=$test"; };
а выход:
test = hello world
Вам нужны скобки после того, как труба будет заключать задание на проверку и эхо.
Без фигурных скобок назначение для проверки (после трубы) находится в одной оболочке, а эхо-тест = $test - в отдельной оболочке, которая не знает об этом назначении. Вот почему вы получили "test =" в выводе вместо "test = hello world".
Потому что я влюбился в это, я хотел бы оставить заметку. Я нашел эту ветку, потому что мне нужно переписать старый sh script для совместимости с POSIX. Это в основном означает обход проблемы с трубкой/подоболочкой, введенной POSIX, путем перезаписи кода следующим образом:
some_command | read a b c
в
read a b c << EOF
$(some_command)
EOF
И код вроде этого:
some_command |
while read a b c; do
# something
done
в
while read a b c; do
# something
done << EOF
$(some_command)
EOF
Но последнее не действует на пустом входе. При старой записи цикл while не вводится на пустом входе, но в POSIX обозначение это! Я думаю, что это связано с новой линией перед EOF, которые нельзя опустить. Код POSIX, который ведет себя как старое обозначение выглядит следующим образом:
while read a b c; do
case $a in ("") break; esac
# something
done << EOF
$(some_command)
EOF
В большинстве случаев это должно быть достаточно хорошим. Но, к сожалению, это все еще ведет себя не так, как старые обозначения если some_command печатает пустую строку. В старой записи выполняется тело while и в нотации POSIX мы ломаемся перед телом.
Подход к исправлению может выглядеть так:
while read a b c; do
case $a in ("something_guaranteed_not_to_be_printed_by_some_command") break; esac
# something
done << EOF
$(some_command)
echo "something_guaranteed_not_to_be_printed_by_some_command"
EOF
Связывание чего-либо с выражением, связанным с назначением, не ведет себя так.
Вместо этого попробуйте:
test=$(echo "hello world"); echo test=$test
Следующий код:
echo "hello world" | ( test=($(< /dev/stdin)); echo test=$test )
тоже будет работать, но после него откроется еще одна новая оболочка, где
echo "hello world" | { test=($(< /dev/stdin)); echo test=$test; }
не будет.
Мне пришлось отключить управление заданиями, чтобы использовать метод chepnars (я запускал эту команду из терминала):
set +m;shopt -s lastpipe
echo "hello world" | read test; echo test=$test
echo "hello world" | test="$(</dev/stdin)"; echo test=$test
lastpipe
Если установлено, и управление заданиями не активно, оболочка запускает последнюю команду конвейера, не выполняемого в фоновом режиме в текущей оболочке окружающая среда.
Примечание. Управление заданием по умолчанию отключено в неинтерактивной оболочке и, следовательно, вам не нужен set +m
внутри script.
Я думаю, вы пытались написать оболочку script, которая могла бы принимать входные данные из stdin. но пока вы пытаетесь сделать это inline, вы потерялись, пытаясь создать эту test = variable. Я думаю, что нет смысла делать это inline, и почему он не работает так, как вы ожидаете.
Я пытался уменьшить
$( ... | head -n $X | tail -n 1 )
чтобы получить определенную строку из разных входных данных. поэтому я могу напечатать...
cat program_file.c | line 34
поэтому мне нужна небольшая программа оболочки, способная читать из stdin. как вы.
22:14 ~ $ cat ~/bin/line
#!/bin/sh
if [ $# -ne 1 ]; then echo enter a line number to display; exit; fi
cat | head -n $1 | tail -n 1
22:16 ~ $
там вы идете.
Вот альтернативное решение, которое также можно использовать при чтении более одной переменной и когда в трубе больше слов, чем число считываемых переменных.
eval $(echo hickery dickery dock the mouse | { read A B C ; echo A=\"${A}\" B=\"${B}\" C=\"${C}\" ; })
echo $A
hickery
echo $B
dickery
echo $C
dock the mouse
Как насчет этого:
echo "hello world" | echo test=$(cat)