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