Чтение вывода команды в переменную в оболочке, разбивает текст по каждому пробелу, а не только по новой строке

Чтение вывода команды в переменную в оболочке, разбивает текст по каждому пробелу, а не только по новой строке

Я использую sh (не bash/csh) на FreeBSD 11 и не понимаю этого:

В консоли

Команда:zpool status -v

Результат:

  pool: My_pool
 state: ONLINE
status: One or more devices is currently being resilvered.  The pool will
        continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.

Разбиение на строки и построчная печать в скрипте:

#!/bin/sh
zp="$(zpool status -v)"
for line in $zp; do
  echo "$line%"
done

Результат:

pool:%
My_pool%
state:%
ONLINE%
status:%
One%
or%
more%
devices%
is%
currently%
being%
resilvered.%
The%
pool%
will%
continue%
to%
function,%
possibly%
in%
a%
degraded%
state.%
action:%
Wait%

Согласно всему, что я смог найти, синтаксис, который я использую, верен для sh и должен считывать по строке, а не по слову.

Что я упускаю?

решение1

Я озадачен тем, что вы нашли. Синтаксис, который вы использовали, верен для sh и разделяет по пробелам, а не только по новым строкам. Это широко документировано. Думать, что он разделяет по переносам строк, не является популярным заблуждением. (Не понимать, что он разделяет, является популярным заблуждением.)

Точнее, оставление переменной расширения без кавычек, как в $zp, иногда называют оператором «split+glob». Вот что он делает:

  1. Возьмем значение переменной. Это строка.
  2. Разделить это значение на каждом символе-разделителе полей. Символ-разделитель полей — это тот, который находится в значении переменной IFS; по умолчанию эта переменная содержитпробел, табуляция и новая строка. (Это значение по умолчанию также используется, если переменная не задана.) Результатом является список строк.
  3. Рассматривайте каждый элемент списка как шаблон wildcard (glob). Если он соответствует одному или нескольким именам файлов, этот элемент заменяется списком соответствующих имен файлов. В противном случае элемент остается один.

Например, если каталог содержит файлы с именами foo, barи baz, то следующий скрипт выведет три строки: a*, barи baz.

zq='a* b*'
for word in $zq; do echo "$word"; done

Если вы хотите разбить строку на новой строке, то вы можете сделать это, установив IFSновую строку и отключив расширение подстановочных знаков.

#!/bin/sh
set -f
IFS='
'
zq=…
for line in $zq; do
done

Это применимо ко всем вариантам sh и csh.

Смотрите такжеПочему мой скрипт оболочки тормозит пробелы и другие специальные символы?

решение2

Чтобы прочитать строки, используйте встроенную в вашу оболочку функциючитатьфункция,

$помогите прочитать | голова -2

читать: читать [-ers] [-u fd] [-t тайм-аут] [-p prompt] [-a array] [-n nchars] [-d delim] [name ...]

Одна строка считывается из стандартного ввода или из файлового дескриптора FD, если указана опция -u

Поместите это в whileцикл и, вуаля, у вас есть строки:

$ wc -l -w ~/.profile 
  24      47 /Users/jklowden/.profile
$ for W in $(cat ~/.profile); do echo $W; done | wc -l 
  47
$ while read L; do echo $L; done < ~/.profile | wc -l 
  24

Связанный контент