Answers:
某些人有错误的概念,read
即读取行的命令。不是。
read
从(可能是反斜杠连续的)行中读取单词,其中的单词是$IFS
定界的,反斜杠可用于转义定界符(或继续行)。
通用语法为:
read word1 word2... remaining_words
read
一次读取标准输入一个字节直到找到一个未转义换行符(或输入结束),分裂,根据复杂的规则并存储分割的结果为$word1
,$word2
... $remaining_words
。
例如在类似的输入上:
<tab> foo bar\ baz bl\ah blah\
whatever whatever
并用默认值$IFS
,read a b c
将会分配:
$a
⇐ foo
$b
⇐ bar baz
$c
⇐ blah blahwhatever whatever
现在,如果仅传递一个参数,则不会变为read line
。还在read remaining_words
。反斜杠处理仍然完成,IFS空格字符仍从开头和结尾删除。
该-r
选项删除反斜杠处理。所以上面的相同命令-r
将分配
$a
⇐ foo
$b
⇐ bar\
$c
⇐ baz bl\ah blah\
现在,对于拆分部分,重要的是要认识到有两类字符$IFS
:IFS空格字符(即空格和制表符(以及换行符,尽管在这里除非使用-d无关紧要),否则也会发生)设为$IFS
)和其他默认值。这两类字符的处理方式不同。
随着IFS=:
(:
是不是IFS空白字符),如输入:foo::bar::
将被分裂成""
,"foo"
,""
,bar
和""
(和一个额外的""
一些实现方式虽然不除无所谓read -a
)。如果将其替换:
为空格,则拆分为only foo
和bar
。那就是前导和尾随的被忽略,并且它们的序列被视为一个。将中的空白字符和非空白字符结合使用时,还有其他规则$IFS
。一些实现可以通过将IFS中的字符加倍(IFS=::
或IFS=' '
)来添加/删除特殊处理。
因此,在这里,如果我们不希望删除开头和结尾的未转义的空白字符,则需要从IFS中删除那些IFS空白字符。
即使使用IFS非空白字符,如果输入行包含这些字符中的一个(只有一个),并且它是该行中的最后一个字符(IFS=: read -r word
例如foo:
),则使用POSIX shell(不是zsh
某些pdksh
版本),该输入之所以被视为一个foo
单词,是因为在这些shell中,这些字符$IFS
被视为终结符,因此word
将包含foo
,而不是foo:
。
因此,使用read
内置方法读取一行输入的规范方法是:
IFS= read -r line
(请注意,对于大多数read
实现,该方法仅适用于文本行,因为除中不支持NUL字符zsh
)。
使用var=value cmd
语法可确保IFS
仅在该cmd
命令期间设置不同的设置。
该read
内置由Bourne shell的引入,已经读的话,而不是行。现代POSIX外壳有一些重要的区别。
Bourne shell read
不支持-r
选项(这是Korn shell引入的),因此除了使用类似的东西对输入进行预处理之外,没有其他方法可以禁用反斜杠处理sed 's/\\/&&/g'
。
Bourne shell没有两类字符的概念(再次由ksh引入)。在Bourne Shell中,所有字符都与ksh中的IFS空格字符进行相同的处理,即IFS=: read a b c
在输入foo::bar
上将类似分配bar
给$b
,而不是空字符串。
在Bourne shell中,具有:
var=value cmd
如果cmd
是内置的(如read
is),则在完成后var
仍设置为。这一点特别重要,因为在Bourne shell中,它用于拆分所有内容,而不仅仅是扩展。另外,如果您从Bourne外壳程序中删除空格字符,将不再起作用。value
cmd
$IFS
$IFS
$IFS
"$@"
在Bourne Shell中,重定向复合命令会使它在子Shell中运行(在最早的版本中,即使类似read var < file
或exec 3< file; read var <&3
不起作用的东西),因此在Bourne Shell中很少read
用于除终端上的用户输入以外的任何内容(在该行继续处理有意义的地方)
某些Unices(例如HP / UX,也有in in util-linux
)仍然具有line
读取一行输入的命令(在Single UNIX Specification版本2之前,它一直是标准UNIX命令)。
基本上与一次head -n 1
读取相同,只是一次读取一个字节以确保读取的行不超过一行。在这些系统上,您可以执行以下操作:
line=`line`
当然,这意味着产生一个新进程,执行一个命令并通过管道读取其输出,因此效率比ksh差IFS= read -r line
很多,但仍然更加直观。
sh
差异之间的洞察力对于编写可移植脚本也很有用!)
bash-4.4.19
,while read -r; do echo "'$REPLY'"; done
起作用while IFS= read -r line; do echo "'$line'"; done
。
read
读取行是错误的,则还必须有其他东西。这个无误的概念是什么?还是从技术上来说,第一个语句是正确的,但实际上,无误的概念是:“ read是从一行中读取单词的命令。由于它功能如此强大,您可以通过执行以下操作从文件中读取行:IFS= read -r line
”
这里有两个概念在起作用:
IFS
是输入字段分隔符,这表示将根据中的字符对读取的字符串进行分割IFS
。在命令行上,IFS
通常是空格字符,这就是命令行在空格处分割的原因。VAR=value command
意味着“修改命令环境,使其VAR
具有值value
”。基本上,该命令command
将VAR
具有值value
,但此后执行的任何命令仍将VAR
具有其先前的值。换句话说,将仅针对该语句修改该变量。因此,在执行操作时IFS= read -r line
,您正在将其设置IFS
为一个空字符串(不使用任何字符来拆分,因此不会进行拆分),以便read
读取整行并将其视为分配给line
变量的一个单词。IFS
所做的更改仅影响该语句,因此所有后续命令都不会受到更改的影响。
虽然该命令是正确的,并会工作打算,设置IFS
在这种情况下是不是 强权1没有必要。如内置部分的bash
手册页中所述read
:
从标准输入中读取一行,并将第一个单词分配给名字,第二个单词分配给名字,依此类推,剩下的单词及其中间的分隔符分配给姓氏。如果从输入流中读取的单词少于名称,则为其余名称分配空值。中的字符
IFS
用于将行拆分为单词。[...]
由于您只有line
变量,因此无论如何都会为每个单词赋值,因此,如果不需要任何前面和结尾的空白字符1,则只需编写read -r line
并完成操作即可。
[1]举例来说,一个unset
或默认$IFS
值将如何导致read
使用引导/跟踪IFS空格,您可以尝试:
echo ' where are my spaces? ' | {
unset IFS
read -r line
printf %s\\n "$line"
} | sed -n l
运行它,您会发现,如果IFS
未设置,则前面和后面的字符将无法生存。此外,如果$IFS
要在脚本的较早位置进行修改,可能会发生一些奇怪的事情。
您应该分两部分阅读该语句,第一部分清除IFS变量的值,即等同于更具可读性IFS=""
,第二部分line
从stdin中读取变量read -r line
。
此语法的特定之处在于IFS的影响是过时的,仅对read
命令有效。
除非我丢失了某些内容,否则在特定情况下,清除IFS
不会起作用,尽管无论IFS
设置为什么,整行都将在line
变量中读取。仅在将多个变量作为参数传递给read
指令的情况下,行为才会发生变化。
编辑:
的-r
是有允许输入与截至\
进行特殊加工不,即,对于要被包括在所述反斜线line
变量,而不是作为连续符,以允许多行输入。
$ read line; echo "[$line]"
abc\
> def
[abcdef]
$ read -r line; echo "[$line]"
abc\
[abc\]
清除IFS具有防止读取以修剪潜在的前导和尾随空格或制表符的副作用,例如:
$ echo " a b c " | { IFS= read -r line; echo "[$line]" ; }
[ a b c ]
$ echo " a b c " | { read -r line; echo "[$line]" ; }
[a b c]
感谢rici指出了这种差异。
read -r line
则在将输入分配给line
变量之前将修剪前导和尾随空格。
IFS= read a b <<< 'aa bb' ; echo "-$a-$b-"
将显示-aa bb--