Q1.场分裂。

Q1.场分裂。

本网站和 StackOverflow 上的以下几个主题有助于理解IFS其工作原理:

但我还有一些简短的问题。我决定在同一篇文章中询问他们,因为我认为这可能会帮助更好的未来读者:

Q1. IFS通常在“字段分割”的上下文中讨论。是场分裂分词

问题2:POSIX 规范

如果IFS的值为空,则不进行字段分割。

设置IFS=和设置为null一样吗IFS?这就是设置为 a 的意思吗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

如果我们将第一行更改为

while read -r line # Use the default IFS value

或者:

while IFS=' ' read -r line

答案1

  1. 是的,它们是一样的。
  2. 是的。
  3. 在 bash 和类似的 shell 中,您可以执行类似的操作IFS=$' \t\n'。否则,您可以使用 插入文字控制代码[space] CTRL+V [tab] CTRL+V [enter]。但是,如果您打算执行此操作,最好使用另一个变量来临时存储旧IFS值,然后再恢复它(或使用语法暂时覆盖一个命令的旧值var=foo command)。
    • 第一个代码片段会将读取的整行逐字放入 中$line,因为没有字段分隔符来执行分词。但请记住,由于许多 shell 使用 cstring 来存储字符串,因此 NUL 的第一个实例仍可能导致其出现过早终止的情况。
    • 第二个代码片段可能不会将输入的精确副本放入$line.例如,如果有多个连续的字段分隔符,它们将被制作为第一个元素的单个实例。这通常被认为是周围空白的丢失。
    • 第三个代码片段的作用与第二个相同,只是它只会在空格上分割(不是通常的空格、制表符或换行符)。

答案2

问1:是的。 “字段分割”和“字分割”是同一概念的两个术语。

问2:是的。如果IFS未设置(即在 之后unset IFS),则相当于IFS设置为$' \t\n'(空格、制表符和换行符)。如果IFS设置为空值(这就是“null”在这里的意思)(即在IFS=IFS=''或后面IFS=""),则根本不执行任何字段分割(并且$*,通常使用 的第一个字符$IFS,使用空格字符)。

Q3:如果你想有默认IFS行为,你可以使用unset IFS.如果要IFS显式设置此默认值,可以将文字字符空格、制表符、换行符放在单引号中。在 ksh93、bash 或 zsh 中,您可以使用IFS=$' \t\n'.可移植的是,如果您想避免源文件中出现文字制表符,您可以使用

IFS=" $(echo t | tr t \\t)
"

Q4:IFS设置为空值时,read -r line设置line为除终止换行符之外的整行。使用 时IFS=" ",行首和行尾的空格将被修剪。使用默认值 时IFS,制表符和空格将被修剪。

答案3

Q1.场分裂。

字段分割与字分割相同吗?

是的,两者都指向同一个想法。

Q2:什么时候IFS 空?。

设置IFS=''与 null 相同,也与空字符串相同吗?

是的,这三个含义相同:不应执行字段/字拆分。此外,这会影响打印字段(与 一样echo "$*"),所有字段都将连接在一起,没有空格。

Q3:(a 部分)取消设置 IFS。

在 POSIX 规范中,我阅读以下:

如果未设置 IFS,shell 的行为将如同 IFS 的值是<空格><制表符><换行>

这完全等同于:

使用 时unset IFS,shell 的行为就像 IFS 是默认的一样。

这意味着“字段分割”将与默认 IFS 值完全相同,或者未设置。
这并不意味着 IFS 在所有条件下都以相同的方式工作。更具体地说,执行OldIFS=$IFS会将 var 设置OldIFS无效的,不是默认值。尝试将 IFS 设置回来,因为这样,IFS=OldIFS会将 IFS 设置为 null,而不是像以前一样保持未设置状态。小心 !!。

Q3:(b 部分)恢复 IFS。

如何将 IFS 的值恢复为默认值。假设我想恢复 IFS 的默认值。我怎么做? (更具体地说,我如何参考<选项卡><换行>?)

对于 zsh、ksh 和 bash (AFAIK),IFS 可以设置为默认值:

IFS=$' \t\n'        # works with zsh, ksh, bash.

完成后,您无需再阅读其他内容。

但如果需要为sh重新设置IFS,可能会变得复杂。

让我们从最简单到没有缺点(复杂性除外)的方式来看看。

1.- 取消设置 IFS。

我们可以unset IFS(阅读上面的第三季度 a 部分)。

2.- 交换字符。

作为一种解决方法,交换制表符和换行符的值可以更简单地设置 IFS 的值,然后以等效的方式工作。

将 IFS 设置为<空格><换行><制表符>:

sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd'      # Works.

3.- 简单吗?解决方案:

如果有需要正确设置 IFS 的子脚本,您始终可以手动编写:

IFS='   
'

手动输入的序列是:IFS='spacetabnewline',上面实际上已正确输入的序列(如果您需要确认,请编辑此答案)。但是从浏览器复制/粘贴将会中断,因为浏览器会挤压/隐藏空白。这使得共享上面编写的代码变得困难。

4.- 完整的解决方案。

要编写可以安全复制的代码通常涉及明确的可打印转义。

我们需要一些“产生”预期值的代码。但是,即使概念上正确,此代码也不会设置尾随\n

sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd'      # wrong.

发生这种情况是因为,在大多数 shell 下,所有尾随换行符$(...)`...`命令替换在扩展时都会被删除。

我们需要使用一个诡计对于 sh:

sh -c 'IFS="$(printf " \t\nx")"; IFS="${IFS%x}"; printf "$IFS"|xxd'  # Correct.

另一种方法可能是将 IFS 设置为 bash 中的环境值(例如),然后调用 sh(接受通过环境设置 IFS 的版本),如下所示:

env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'

简而言之,sh 使将 IFS 重置为默认值成为一次相当奇怪的冒险。

Q4:在实际代码中:

最后,这段代码是如何实现的:

while IFS= read -r line
do
    echo $line
done < /path_to_text_file

如果我们将第一行更改为

while read -r line # Use the default IFS value

或者:

while IFS=' ' read -r line

第一:我不知道echo $line(未引用 var 的)是否存在于 porpouse 上。它引入了 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

我得到:

$ ./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

第一个值就是正确的值IFS='spacetabnewline'

下一行是 var$a具有的所有十六进制值,末尾有一个换行符“0a”,因为它将被赋予每个读取命令。

IFS 为空的下一行不执行任何“字段分割”,但换行符被删除(如预期)。

接下来的三行,由于 IFS 包含空格,因此删除初始空格并将 var 行设置为剩余的余额。

最后四行显示了未加引号的变量将执行的操作。这些值将被分割为(几个)空格,并打印为:bar,baz,qux,

答案4

unset IFS确实清除 IFS,即使 IFS 此后被假定为“\t\n”:

$ echo "'$IFS'"
'   
'
$ IFS=""
$ echo "'$IFS'"
''
$ unset IFS
$ echo "'$IFS'"
''
$ IFS=$' \t\n'
$ echo "'$IFS'"
'   
'
$

在 bash 版本 4.2.45 和 3.2.25 上进行了测试,具有相同的行为。

相关内容