Подтвердить что ты не робот

Итерации по строкам вместо слов в цикле for оболочки script

Ниже приведена оболочка script, чтобы прочитать все DSF, присутствующие в поле. Но поскольку строка имеет пробелы, она отображает их в разных строках. Для тех из вас, кто не понимает ioscan -m dsf, замените его на ls -ltr, тогда вывод таков, что разрешения и имена отображаются в другой строке, но я хочу, чтобы они были в одной строке.

#!/usr/bin/ksh

for a in `ioscan -m dsf`
do
 echo  $a
done
4b9b3361

Ответ 1

Цикл for не предназначен для зацикливания "строк". Вместо этого он зацикливается на "слова" или "поля".

Идиоматичен способом цикла по линии является использование while цикла в сочетании с read.

ioscan -m dsf | while read -r line
do
  printf '%s\n' "$line"
done

Обратите внимание, что цикл while находится в подоболочке из-за конвейера. Это может вызвать некоторую путаницу с переменной областью действия. В bash вы можете обойти это, используя подстановку процессов.

while read -r line
do
  printf '%s\n' "$line"
done < <(ioscan -m dsf)

см. также http://mywiki.wooledge.org/BashFAQ/024


Цикл for разделяет значения для циклического использования символов в переменной $IFS (внутренний разделитель полей) в качестве разделителей. Обычно $IFS содержит пробел, табуляцию и символ новой строки. Это означает, что цикл for будет зацикливаться на "словах", а не на строках.

Если вы настаиваете на использовании цикла for для циклического перебора строк, вы должны изменить значение $IFS только на новую строку. Но если вы сделаете это, вы должны сохранить старое значение $IFS и восстановить его после цикла, потому что многие другие вещи также зависят от $IFS.

OLDIFS="$IFS"
IFS=$'\n' # bash specific
for line in $(ioscan -m dsf)
do
  printf '%s\n' "$line"
done
IFS="$OLDIFS"

в оболочках POSIX, которые не имеют кавычек ANSI-C ($'\n'), вы можете сделать это следующим образом:

IFS='
'

то есть: поставить новую строку между кавычками.

В качестве альтернативы вы можете использовать подоболочку для хранения изменения $IFS:

(
  # changes to variables in the subshell stay in the subshell
  IFS=$'\n'
  for line in $(ioscan -m dsf)
  do
    printf '%s\n' "$line"
  done
)
# $IFS is not changed outside of the subshell

Но будьте осторожны, команда в цикле может сама по себе зависеть от некоторых нормальных настроек для $IFS. Затем вы должны восстановить $IFS перед выполнением команды и установить снова перед следующим циклом или чем-то подобным. Я не рекомендую возиться с $IFS. Слишком много команд зависят от некоторых нормальных значений в $IFS и их изменение - это бесконечный кошмар неясной охоты за ошибками.

Смотрите также:

Ответ 2

Использование для

for l in $() выполняет разбиение слов на основе IFS:

$ for l in $(printf %b 'a b\nc'); do echo "$l"; done
a
b
c
$ IFS=$'\n'; for l in $(printf %b 'a b\nc'); do echo "$l"; done
a b
c

IFS не нужно возвращать, если он не используется позже.

for l in $() также выполняет расширение имени пути:

$ printf %b 'a\n*\n' > file.txt
$ IFS=$'\n'
$ for l in $(<file.txt); do echo "$l"; done
a
file.txt
$ set -f; for l in $(<file.txt); do echo "$l"; done; set +f
a
*

Если IFS=$'\n', отбрасываются и сбрасываются строки:

$ printf %b '\n\na\n\nb\n\n' > file.txt
$ IFS=$'\n'; for l in $(<file.txt); do echo "$l"; done
a
b

$(cat file.txt) (или $(<file.txt)) также считывает весь файл в память.

Использование чтения

Без -r обратные косые черты используются для продолжения линии и удаляются перед другими символами:

$ cat file.txt
\1\\2\
3
$ cat file.txt | while read l; do echo "$l"; done
1\23
$ cat file.txt | while read -r l; do echo "$l"; done
\1\\2\
3

Символы в IFS удаляются из начала и конца строк, но не свертываются:

$ printf %b '1  2 \n\t3\n' | while read -r l; do echo "$l"; done
1  2
3
$ printf %b ' 1  2 \n\t3\n' | while IFS= read -r l; do echo "$l"; done
 1  2 
    3

Если последняя строка не заканчивается символом новой строки, read присваивает ей l, но выходит перед телом цикла:

$ printf 'x\ny' | while read l; do echo $l; done
x
$ printf 'x\ny' | while read l || [[ $l ]]; do echo $l; done
x
y

Если цикл while находится в конвейере, он также находится в подоболочке, поэтому переменные не видны вне него:

$ x=0; seq 3 | while read l; do let x+=l; done; echo $x
0
$ x=0; while read l; do let x+=l; done < <(seq 3); echo $x
6
$ x=0; x=8 | x=9; echo $x
0