linux 为什么不能使用cat逐行读取每个行都有分隔符的文件

huwehgph  于 2023-02-03  发布在  Linux
关注(0)|答案(6)|浏览(210)

我有一个文本文件,其中包含这样的内容:

abc 123, comma
the quick brown fox
jumped over the lazy dog
comma, comma

我写了个剧本

for i in `cat file`
do
   echo $i
done

由于某种原因,脚本的输出不是逐行输出文件,而是在逗号和换行符处将其断开。为什么cat或“for blah in cat xyz“会这样做?如何使其不这样做?我知道我可以使用

while read line
do
   blah balh blah
done < file

但是我想知道为什么cat或“for blah in”这样做来加深我对unix命令的理解。Cat的手册页没有帮助我,在bash手册中查找for或循环也没有得到任何答案(http://www.gnu.org/software/bash/manual/bashref.html)。提前感谢您的帮助。

798qvoo8

798qvoo81#

问题不在cat,也不在for循环本身;关键在于反引号的使用。当你这样写的时候:

for i in `cat file`

或(更好):

for i in $(cat file)

或(单位:bash):

for i in $(<file)

shell执行命令并将输出捕获为字符串,以$IFS中的字符分隔单词。如果您希望将行输入到$i,则必须修改IFS或使用while循环。如果存在处理的文件过大的危险,则while循环更好;它不必一次将整个文件读入内存,这与使用$(...)的版本不同。

IFS='
'
for i in $(<file)
do echo "$i"
done

"$i"的引号通常是一个好主意。在这个上下文中,对于修改后的$IFS,引号实际上并不重要,但即使如此,好习惯仍然是好习惯。在下面的脚本中,引号很重要:

old="$IFS"
IFS='
'
for i in $(<file)
do
   (
   IFS="$old"
   echo "$i"
   )
done

当数据文件在单词之间包含多个空格时:

$ cat file
abc                  123,         comma
the   quick   brown   fox
jumped   over   the   lazy   dog
comma,   comma
$

输出:

$ sh bq.sh
abc                  123,         comma
the   quick   brown   fox
jumped   over   the   lazy   dog
comma,   comma
$

不带双引号:

$ cat bq.sh
old="$IFS"
IFS='
'
for i in $(<file)
do
   (
   IFS="$old"
   echo $i
   )
done
$ sh bq.sh
abc 123, comma
the quick brown fox
jumped over the lazy dog
comma, comma
$
bzzcjhmw

bzzcjhmw2#

cat filename | while read i
do
    echo $i
done
u0njafvf

u0njafvf3#

您可以使用IFS变量来指定您想要一个换行符作为字段分隔符:

IFS=$'\n'
for i in `cat file`
do
   echo $i
done
jjjwad0x

jjjwad0x4#

for循环加上内部字段分隔符(IFS)的更改将按预期读取文件
对于输入

abc 123, comma
the quick brown fox
jumped over the lazy dog
comma, comma

与IFS更改耦合的For循环

old_IFS=$IFS
IFS=$'\n'
for i in `cat file`
do
        echo $i
done
IFS=$old_IFS

导致

abc 123, comma
the quick brown fox
jumped over the lazy dog
comma, comma
bybem2ql

bybem2ql5#

IFS -内部字段分隔符可以设置为得到你想要的。
要一次读取整行,请用途:IFS="”

13z8s7eq

13z8s7eq6#

为了防止新的IFS设置改变你的shell,你不需要做任何其他提到的事情...
通过将命令放在圆括号中来使用子shell(我也更喜欢使用转义序列来设置IFS;这使得更容易阅读所做的事情):

(IFS=$'\n'; for i in $(cat file); do echo $i; done)

分号代替了必要的换行符,圆括号打开了一个subshell,$(...)语法用输出到stdout代替了subshell调用,单引号前的$导致了单引号内的转义序列解释(不适用于双引号,是一种bashism,即不适用于任何其他POSIX shell)。
或者,您可以

(IFS=$'\n'
for i in $(cat file); do
    echo $i
done)

或者甚至可以将do或圆括号放在它们自己的行上,如果这是您想要的。

相关问题