如何在'for'循环中读取带空格的完整行


127

我正在尝试for为文件运行循环,我想显示整行。但是,它只显示最后一个单词。我想要完整的线。

for j in `cat ./file_wget_med`

do
echo $j

done

运行后的结果:

Found.

这是我的数据:

$ cat file_wget_med
2013-09-11 14:27:03 ERROR 404: Not Found.

Answers:


177

for循环会在看到空格,制表符或换行符之类的空白时拆分。因此,您应该使用IFS(内部字段分隔符)

IFS=$'\n'       # make newlines the only separator
for j in $(cat ./file_wget_med)    
do
    echo "$j"
done
# Note: IFS needs to be reset to default!

5
并且要注意IFS上的单引号,因为IFS = $“ \ n”也会拆分“ nn”字符串。我发现设置IFS比使用更复杂的语法或函数更可靠。
erm3nda 2015年

23
显然,建议unset IFS随后执行此操作,以使其他命令不会在同一shell中受到影响。
BorisDäppen2015年

2
是什么$意思IFS=$'\n'

2
$使换行符在字符串内起作用。这也应该local IFS=$'\n'在函数内部完成。
安迪·雷

1
@Renn多数民众赞成$'...'称为“ ANSI-C引用
αғsнιη

82

for默认情况下,循环在任何空格(空格,制表符,换行符)上分割;一次在一行上工作的最简单方法是改为使用while read循环,该循环在换行符上分割:

while read i; do echo "$i"; done < ./file_wget_med

希望您的命令每行吐出一个单词(这就是我用自己的文件测试出来的结果)。如果发生其他事情,我不确定是什么原因引起的。


7
for i in `ls`; do echo $1; done通过将输出传递给while命令:,这也是一个很好的选择ls|while read i; do echo $i; done。这适用于带空格的文件/文件夹名称,而无需更改环境变量。很好的答案。
jishi

而while并没有很好地处理第一行和最后一行,以及IFS的for循环效果
不佳

@jishi因为它主要是为显示而设计的,并且对终端窗口的大小等具有响应性,ls所以不被认为可以安全地与|(管道操作员)一起使用。find除了为此目的更安全之外,它还更加灵活。
SeldomNeedy

3
这是一个很好的答案,我用它将我在Mac OSX上开发的IOS应用的列表转换成PLIST条目。我使用pbpaste- >pbpaste | while read t; do echo "<string>$t</string>"; done
Big Rich

3
ls -1可以用于|保证每行单格式输出
AndreyS Scherbakov

19
#!/bin/bash
files=`find <subdir> -name '*'`
while read -r fname; do
    echo $fname
done <<< "$files"

经过验证的工作方式,而不是您可能想要的那种班轮,但不可能做到如此优雅。


1
一个字符串!IMO
Eliran Malka

5

这是对Mitchell Currie的回答的略微扩展,由于副作用范围小,我喜欢这样做,从而避免了必须设置变量的情况:

#!/bin/bash
while read -r fname; do
    echo $fname
done <<< "`find <subdir>`"

1
您可以删除-name '*',这将是相同的,不是吗?
wjandrea

1

我会这样写:

cat ./file_wget_med | while read -r j
do
    echo $j
done

因为它需要对原始脚本进行最少的更改(使用解决方案除外IFS,但它bash不仅修改此循环控制语句的行为)。


1
管道,cat这里是不必要的。while循环接受重定向就好了,这就是为什么这通常写为while IFS= read -r line; do...done < input.txt
Sergiy Kolodyazhnyy 18/12/31

0

Mapfile是一种将行从文件读入索引数组的便捷方法,它不像读取那样可移植,但速度稍快。通过使用for循环,可以避免创建子外壳。

#!/bin/bash

mapfile -t < file.txt

for line in "${MAPFILE[@]}"; do
    echo $line
done

使用管道时请记住,它将while循环放在子shell中。while循环内部的变化(如变量)不会传播到脚本的外部。

例:

#!/bin/bash

a=0
printf %s\\n {0..5} | while read; do
  ((a++))
done
echo $a # 'a' will always be 0.

(更好的解决方案):

#!/bin/bash

b=0
while read; do
  ((b++))
done < <(printf %s\\n {0..5})

echo $b # 'b' equal to 6 (works as expected).

-1

Dandalf非常接近一种功能解决方案,但是永远不要试图将未知输入量(即find ~/.gdfuse -name '*')的结果分配给变量!或者,至少试图通过数组变量来做这样的事情;如果您坚持要这么懒!因此,这是Dandalf的解决方案,无需进行危险的操作;一站式

while read -r fname; do
  echo $fname;
done <<< `find ~/.gdfuse -name '*'

嘿@odoncaoa,我试图在没有双引号的情况下执行find命令,但是它使所有结果都显示为一行。我对该命令的“未知输入量”以及所涉及的危险感到困惑。您能否提供一个危险示例,以及在理论上如何发生这些危险?我真的不希望对此感到懒惰,我对其他编程语言更加满意。
丹达夫
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.