Вопрос по linux, shell – Перебирайте строки вместо слов в цикле for сценария оболочки

26

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

#!/usr/bin/ksh

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

Ваш Ответ

2   ответа
11

Using for

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)) также читает весь файл в память.

Using read

Без -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

Если последняя строка не заканчивается новой строкой, чтение присваивает ей 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
35

for Цикл не предназначен для зацикливания строк.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)

Цикл 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

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

Error: User Rate Limit Exceeded

Похожие вопросы