Вывод команды конвейера Bash в цикл суммы

Приступая к bash, мне это нравится, но кажется, что есть много тонкостей, которые в конечном итоге сильно влияют на функциональность, и еще много чего, в любом случае, вот мой вопрос:

Я знаю, что это работает:

total=0
for i in $(grep number some.txt | cut -d " " -f 1); do
    (( total+=i ))
done

Но почему это не так?:

grep number some.txt | cut -d " " -f 1 | while read i; do (( total+=i )); done

некоторые.txt:

1 number
2 number
50 number

как цикл for, так и цикл while получают 1, 2 и 50 по отдельности, но цикл for показывает, что общая переменная в конце равна 53, а в коде цикла while она просто остается равной нулю. Я знаю, что здесь мне не хватает некоторых фундаментальных знаний, пожалуйста, помогите мне.

Я также не вижу различий в трубопроводах, например, если я запускаю

grep number some.txt | cut -d " " -f 1 | while read i; echo "-> $i"; done

Я получаю ожидаемый результат

-> 1
-> 2
-> 50

Но если бежать так

while read i; echo "-> $i"; done <<< $(grep number some.txt | cut -d " " -f 1)

затем вывод меняется на

-> 1 2 50

Мне это кажется странным, поскольку grep выводит результат отдельными строками. Как бы это не было двусмысленно, если бы у меня был файл только с номерами 1 2 3 в отдельных строках, и я запускал

while read i; echo "-> $i"; done < someother.txt

Затем вывод будет напечатан эхом в разных строках, как и ожидалось в предыдущем примере. Я знаю, что ‹ для файлов, а ‹‹‹ для выходных данных команд, но почему существует эта разница в строках?

В любом случае, я надеялся, что кто-то сможет пролить свет на этот вопрос, спасибо за ваше время!


person acib708    schedule 28.01.2015    source источник
comment
В качестве простого примера проблемы во втором вопросе попробуйте: a=$'foo\nbar'; echo $a; echo ===; echo "$a; echo ===; cat <<<$a; echo ===; cat <<<"$a".   -  person Etan Reisner    schedule 28.01.2015
comment
Вы также можете рассчитать сумму первого столбца, используя awk: awk '{a+=$1} END {print a}' some.txt   -  person Marat Talipov    schedule 28.01.2015


Ответы (1)


grep number some.txt | cut -d " " -f 1 | while read i; do (( total+=i )); done

Каждая команда в конвейере выполняется в подоболочке. Это означает, что когда вы помещаете цикл while read в конвейер, любые назначения переменных теряются.

См.: BashFAQ 024 — «Я устанавливаю переменные в цикле, который находится в конвейере. Почему они исчезают после завершения цикла ? Или почему я не могу передать данные для чтения?"

while read i; echo "-> $i"; done <<< "$(grep number some.txt | cut -d " " -f 1)"

Чтобы сохранить новые строки grep, добавьте двойные кавычки. В противном случае результат $(...) подлежит разбиению на слова, в результате чего все пробелы объединяются в отдельные пробелы.

person John Kugelman    schedule 28.01.2015
comment
Другой вариант — set +m; shopt -s lastpipe, но это действительно мутит воду. - person glenn jackman; 28.01.2015