49

以下是读取框中所有 DSF 的 shell 脚本。但是由于该行有空格,因此将它们显示在不同的行中。对于那些不明白的人ioscan -m dsf,将其替换为ls -ltr,然后输出是权限和名称显示在不同的行中,但我希望它们在同一行中。

#!/usr/bin/ksh

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

3 回答 3

65

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"

在没有ANSI-C 引用( $'\n') 的 POSIX shell 中,您可以这样做:

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而改变它是一个无止境的寻虫噩梦。

也可以看看:

于 2012-05-25T10:04:25.207 回答
13

用于

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 循环在管道中,它也在子 shell 中,因此变量在其外部不可见:

$ 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
于 2013-04-22T11:11:38.187 回答
-1

您基本上需要使用它IFS=$'\n',而grep -x不是grep因为它将像等于运算符而不是像运算符一样工作。

于 2020-05-10T20:01:32.263 回答