本站点和 StackOverflow 上的以下几个线程有助于理解IFS工作原理:
但我还有一些简短的问题。我决定在同一篇文章中问他们,因为我认为这可能有助于更好的未来读者:
一季度。 IFS通常在“字段拆分”的上下文中讨论。是场分裂一样的分词?
Q2: POSIX 规范说:
如果IFS 的值为空,则不进行字段拆分。
设置IFS=是否与设置IFS为空相同?这也是将其设置为 an 的意思empty string吗?
Q3:在 POSIX 规范中,我阅读了以下内容:
如果没有设置 IFS,shell 的行为就像 IFS 的值是
<space>, <tab> and <newline>
假设我想恢复IFS. 我怎么做?(更具体地说,我如何引用<tab>和<newline>?)
Q4:最后,这段代码如何:
while IFS= read -r line
do
echo $line
done < /path_to_text_file
Run Code Online (Sandbox Code Playgroud)
如果我们将第一行更改为
while read -r line # Use the default IFS value
Run Code Online (Sandbox Code Playgroud)
或者:
while IFS=' ' read -r line
Run Code Online (Sandbox Code Playgroud)
Chr*_*own 31
IFS=$' \t\n'. 否则,您可以使用[space] CTRL+V [tab] CTRL+V [enter]. 但是,如果您打算这样做,最好使用另一个变量来临时存储旧IFS值,然后再恢复它(或使用var=foo command语法为一个命令临时覆盖它)。$line,因为没有字段分隔符来执行分词。但是请记住,由于许多 shell 使用 cstrings 来存储字符串,因此 NUL 的第一个实例仍然可能导致它过早终止的外观。$line. 例如,如果有多个连续的字段分隔符,它们将被制作成第一个元素的单个实例。这通常被认为是周围空白的丢失。Gil*_*il' 24
Q1:是的。“字段拆分”和“分词”是同一个概念的两个术语。
Q2:是的。如果IFS未设置(即在 之后unset IFS),则等效IFS于设置为$' \t\n'(空格、制表符和换行符)。如果IFS设置为空值(这就是“空”在这里的意思)(即后IFS=或IFS=''或IFS=""),无场分裂全部执行(和$*,通常使用的第一个字符$IFS,使用空格字符)。
Q3:如果你想拥有默认IFS行为,你可以使用unset IFS. 如果要IFS明确设置为该默认值,可以将文字字符空格、制表符、换行符放在单引号中。在 ksh93、bash 或 zsh 中,您可以使用IFS=$' \t\n'. 可移植地,如果您想避免在源文件中包含文字制表符,您可以使用
IFS=" $(echo t | tr t \\t)
"
Run Code Online (Sandbox Code Playgroud)
Q4:IFS设置为空值时,read -r line设置line为除终止换行符外的整行。使用IFS=" ",可以修剪行首和行尾的空格。使用默认值IFS,制表符和空格被修剪。
小智 17
字段拆分与单词拆分相同吗?
是的,两者都指向同一个想法。
设置
IFS=''是否与空相同,也与空字符串相同?
是的,这三个意思都是一样的:不得进行字段/单词拆分。此外,这会影响打印字段(如echo "$*"),所有字段都将连接在一起,没有空格。
在 POSIX 规范中,我阅读了以下内容:
如果没有设置 IFS,shell 的行为就像 IFS 的值是<space><tab><newline>。
这完全等同于:
使用
unset IFS,shell 的行为就像 IFS 是默认值一样。
这意味着“字段拆分”将与默认 IFS 值完全相同,或者未设置。
这并不意味着 IFS 在所有条件下都以相同的方式工作。更具体地说,执行OldIFS=$IFS会将 var 设置OldIFS为null,而不是默认值。并且尝试将 IFS 设置回原处,这样IFS=OldIFS会将 IFS 设置为 null,而不是像以前那样保持未设置状态。小心 !!。
如何将 IFS 的值恢复为默认值。说我想恢复IFS的默认值。我怎么做?(更具体地说,我如何引用<tab>和<newline>?)
对于 zsh、ksh 和 bash (AFAIK),IFS 可以设置为默认值,如下所示:
IFS=$' \t\n' # works with zsh, ksh, bash.
Run Code Online (Sandbox Code Playgroud)
完成了,您无需阅读其他内容。
但是如果你需要为sh重新设置IFS,它可能会变得复杂。
让我们从没有缺点(复杂性除外)的最简单到完整来看看。
我们可以unset IFS(阅读上面的 Q3 部分 a)。
作为一种解决方法,交换制表符和换行符的值可以更简单地设置 IFS 的值,然后它以等效的方式工作。
将 IFS 设置为<space><newline><tab>:
sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd' # Works.
Run Code Online (Sandbox Code Playgroud)
如果有需要正确设置 IFS 的子脚本,您可以随时手动编写:
IFS=” '
手动输入的序列是:IFS='spacetabnewline',上面实际上已正确输入的序列(如果您需要确认,请编辑此答案)。但是从浏览器复制/粘贴会中断,因为浏览器会挤压/隐藏空白。上面写的代码很难共享。
编写可以安全复制的代码通常涉及明确的可打印转义。
我们需要一些“产生”预期值的代码。但是,即使概念上正确,此代码也不会设置尾随\n:
sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd' # wrong.
Run Code Online (Sandbox Code Playgroud)
发生这种情况是因为在大多数 shell 下,所有尾随的换行符$(...)或`...`命令替换都在扩展时被删除。
我们需要对 sh使用一个技巧:
sh -c 'IFS="$(printf " \t\nx")"; IFS="${IFS%x}"; printf "$IFS"|xxd' # Correct.
Run Code Online (Sandbox Code Playgroud)
另一种方法可能是将 IFS 设置为来自 bash 的环境值(例如),然后调用 sh(它接受通过环境设置的 IFS 的版本),如下所示:
env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'
Run Code Online (Sandbox Code Playgroud)
简而言之,sh 使将 IFS 重置为默认值非常奇怪。
最后,这段代码如何:
Run Code Online (Sandbox Code Playgroud)while IFS= read -r line do echo $line done < /path_to_text_file如果我们将第一行更改为
Run Code Online (Sandbox Code Playgroud)while read -r line # Use the default IFS value或者:
Run Code Online (Sandbox Code Playgroud)while IFS=' ' read -r line
第一:我不知道echo $line(没有引用 var)是否存在于海豚身上。它引入了 read 没有的第二级“字段拆分”。所以我会一一回答。:)
使用此代码(以便您确认)。您将需要有用的 xxd:
#!/bin/ksh
# Correctly set IFS as described above.
defIFS="$(printf " \t\nx")"; defIFS="${defIFS%x}";
IFS="$defIFS"
printf "IFS value: "
printf "%s" "$IFS"| xxd -p
a=' bar baz quz '; l="${#a}"
printf "var value : %${l}s-" "$a" ; printf "%s\n" "$a" | xxd -p
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf 'Values quoted :\n' "" # With values quoted:
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS default quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf '%s\n' "Values unquoted :" # Now with values unquoted:
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- unquoted : "
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS defau unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
Run Code Online (Sandbox Code Playgroud)
我得到:
$ ./stackexchange-Understanding-IFS.sh
IFS value: 20090a
var value : bar baz quz -20202062617220202062617a20202071757a2020200a
IFS --x-- : bar baz quz -20202062617220202062617a20202071757a202020
Values quoted :
IFS null quoted : bar baz quz -20202062617220202062617a20202071757a202020
IFS default quoted : bar baz quz-62617220202062617a20202071757a
IFS unset quoted : bar baz quz-62617220202062617a20202071757a
IFS space quoted : bar baz quz-62617220202062617a20202071757a
Values unquoted :
IFS --x-- unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS null unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS defau unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS unset unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS space unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
Run Code Online (Sandbox Code Playgroud)
第一个值只是正确的值 IFS='spacetabnewline'
下一行是 var$a具有的所有十六进制值,最后是一个换行符“0a”,因为它将被赋予每个读取命令。
下一行,IFS 为空,不执行任何“字段拆分”,但换行符被删除(如预期的那样)。
接下来的三行,因为 IFS 包含一个空格,删除初始空格并将 var 行设置为剩余的余额。
最后四行显示了一个不带引号的变量会做什么。这些值将在(几个)空格上拆分,并将打印为:bar,baz,qux,