在shell脚本的for循环中迭代行而不是单词

时间:2012-05-25 05:02:56

标签: linux shell

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

#!/usr/bin/ksh

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

3 个答案:

答案 0 :(得分:37)

for循环不是为了循环而设计的。 for循环遍历“words”或“fields”。

循环使用行的惯用方法是将while循环与read结合使用。

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

请注意,由于管道,while循环位于子shell中。在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"
在没有ANSI-C Quoting$'\n')的POSIX shell中的

,你可以这样做:

IFS='
'

即:在引号之间添加一条实际的新行。

或者,您可以使用子shell将更改包含到$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

另见:

答案 1 :(得分:11)

使用

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))也会将整个文件读取到内存中。

使用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

如果最后一行没有以换行符结尾,则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

答案 2 :(得分:0)

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