处理几个文件的常用方法是——不要为此打我:
for f in $(ls); do …
Run Code Online (Sandbox Code Playgroud)
现在,为了防止带有空格或其他奇怪字符的文件,一种天真的方法是:
find . -type f -print0 | while IFS= read -r -d '' file; …
Run Code Online (Sandbox Code Playgroud)
在这里,-d ''
是设置 ASCII NUL 的缩写,如-d $'\0'
。
但为什么会这样呢?为什么是''
和$'\0'
一样?这是因为 Bash 的 C 根以空字符串总是以空字符结尾吗?
mic*_*has 10
该man page of bash
读取:
-d delim
The first character of delim is used to terminate the
input line, rather than newline.
Run Code Online (Sandbox Code Playgroud)
因为字符串通常以空字符结尾,所以空字符串的第一个字符是空字节。- 我感觉合理。:)
消息来源写道:
static unsigned char delim;
[...]
case 'd':
delim = *list_optarg;
break;
Run Code Online (Sandbox Code Playgroud)
对于空字符串delim
只是空字节。
bash 有两个相互弥补的不足。
写入时$'\0'
,它在内部被视为与空字符串相同。例如:
$ a=$'\0'; echo ${#a}
0
Run Code Online (Sandbox Code Playgroud)
这是因为 bash 在内部将所有字符串存储为C字符串,这些字符串以空字符结尾——空字节标记字符串的结尾。Bash 默默地将字符串截断到第一个空字节(它不是字符串的一部分!)。
# a=$'foo\0bar'; echo "$a"; echo ${#a}
foo
3
Run Code Online (Sandbox Code Playgroud)
当您将字符串作为参数传递给内置-d
选项时read
,bash 只查看字符串的第一个字节。但它实际上并没有检查字符串是否为空。在内部,空字符串表示为仅包含空字节的 1 元素字节数组。因此,bash 不是读取字符串的第一个字节,而是读取这个空字节。
然后,在内部,read
内置函数背后的机制可以很好地处理空字节;它继续逐字节读取,直到找到分隔符。
其他壳的行为不同。例如,ash 和 ksh 在读取输入时忽略空字节。使用 ksh,ksh -d ""
读取直到换行。Shell 旨在很好地处理文本,而不是二进制数据。Zsh 是一个例外:它使用字符串表示来处理任意字节,包括空字节;在 zsh 中,$'\0'
是一个长度为 1 的字符串(但read -d ''
奇怪的是,它的行为类似于read -d $'\0'
)。