Linux 在 shell 脚本的 for 循环中迭代行而不是单词
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10748703/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Iterate over lines instead of words in a for loop of shell script
提问by randeepsp
Following is the shell script to read all the DSF present in the box. But since the line is having spaces, it is displaying them in different lines.
For those of you who dont understand ioscan -m dsf
, replace it by ls -ltr
, then the output is such that the permission and names are displayed in different line, but i want them in the same line.
以下是读取框中存在的所有 DSF 的 shell 脚本。但是由于该行有空格,因此它将它们显示在不同的行中。对于那些不理解的人ioscan -m dsf
,将其替换为ls -ltr
,然后输出使得权限和名称显示在不同的行中,但我希望它们在同一行中。
#!/usr/bin/ksh
for a in `ioscan -m dsf`
do
echo $a
done
采纳答案by lesmana
The for
loop is not designed to loop over "lines". Instead it loops over "words", or "fields".
该for
循环的目的不是要遍历“线”。相反,它会遍历“单词”或“字段”。
The idiomatic way to loop over lines is to use a while
loop in combination with read
.
循环遍历行的惯用方法是将while
循环与read
.
ioscan -m dsf | while read -r line
do
printf '%s\n' "$line"
done
Note that the while loop is in a subshell because of the pipe. This can cause some consfusion with variable scope. In bash you can work around this by using process substitution.
请注意,由于管道的原因,while 循环位于子外壳中。这可能会导致对变量作用域的一些混淆。在 bash 中,您可以通过使用进程替换来解决这个问题。
while read -r line
do
printf '%s\n' "$line"
done < <(ioscan -m dsf)
see also http://mywiki.wooledge.org/BashFAQ/024
另见http://mywiki.wooledge.org/BashFAQ/024
The for loop splits the values to loop over using the characters in the $IFS
(Internal field separator) variable as separators. Usually $IFS
contains a space, a tab, and a newline. That means the for
loop will loop over the "words", not over the lines.
for 循环使用$IFS
(内部字段分隔符)变量中的字符作为分隔符来拆分要循环的值。通常$IFS
包含一个空格、一个制表符和一个换行符。这意味着for
循环将在“单词”上循环,而不是在行上循环。
If you insist on using a for loop to loop over lines you have to change the value of $IFS
to only newline. But if you do this you have to save the old value of $IFS
and restore that after the loop, because many other things also depend on $IFS
.
如果您坚持使用 for 循环遍历行,则必须将 的值更改$IFS
为仅换行。但是如果你这样做,你必须保存 的旧值$IFS
并在循环后恢复它,因为许多其他事情也依赖于$IFS
.
OLDIFS="$IFS"
IFS=$'\n' # bash specific
for line in $(ioscan -m dsf)
do
printf '%s\n' "$line"
done
IFS="$OLDIFS"
in POSIX shells, that have no ANSI-C Quoting($'\n'
), you can do it like this:
在没有ANSI-C 引用( $'\n'
) 的POSIX shell 中,您可以这样做:
IFS='
'
that is: put an actual new line between the quotes.
也就是说:在引号之间放置一个实际的新行。
Alternatively you can use a subshell to contain the change to $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
But beware the command in the loop may itself depends on some sane setting for $IFS
. Then you have to restore the $IFS
before executing the command and set again before the next loop or some such. I do not recommend messing with $IFS
. Too many commands depend on some sane values in $IFS
and changing it is an endless nightmare of obscure bug hunting.
但请注意,循环中的命令本身可能取决于$IFS
. 然后你必须$IFS
在执行命令之前恢复并在下一个循环或类似之前再次设置。我不建议搞乱$IFS
。太多的命令依赖于一些合理的值$IFS
,改变它是一个无止境的错误搜寻的噩梦。
See also:
也可以看看:
回答by Lri
Using for
用于
for l in $()
performs word splitting based on IFS:
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 doesn't have to be set back if it is not used later.
如果以后不使用 IFS,则不必设置回退。
for l in $()
also performs pathname expansion:
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
*
If IFS=$'\n'
, linefeeds are stripped and collapsed:
如果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)
(or $(<file.txt)
) also reads the whole file to memory.
$(cat file.txt)
( 或$(<file.txt)
) 还将整个文件读取到内存中。
Using read
使用读取
Without -r backslashes are used for line continuation and removed before other characters:
没有 -r 反斜杠用于行继续并在其他字符之前删除:
$ cat file.txt
\2\
3
$ cat file.txt | while read l; do echo "$l"; done
1
$ cat file.txt | while read -r l; do echo "$l"; done
\2\
3
Characters in IFS are stripped from the start and end of lines but not collapsed:
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
If the last line doesn't end with a newline, read assigns l to it but exits before the body of the loop:
如果最后一行没有以换行符结尾,则 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
If a while loop is in a pipeline, it is also in a subshell, so variables are not visible outside it:
如果 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
回答by SaiSaranReddy
you need to use this basically IFS=$'\n'
and grep -x
instead of grep
as it will work like a equal to operator instead of like operator.
您需要基本上使用它IFS=$'\n'
,grep -x
而不是grep
因为它会像等于运算符而不是像运算符一样工作。