![使用/读取/写入空字符和转义字符](https://linux22.com/image/71158/%E4%BD%BF%E7%94%A8%2F%E8%AF%BB%E5%8F%96%2F%E5%86%99%E5%85%A5%E7%A9%BA%E5%AD%97%E7%AC%A6%E5%92%8C%E8%BD%AC%E4%B9%89%E5%AD%97%E7%AC%A6.png)
'/'
我知道Linux中的文件名除了两个字符和之外没有任何限制'\0'
。我知道这'/'
是禁止的,因为它是目录分隔符,但是还有其他原因吗?
另外,在我的终端上,我可以创建一个名为 的文件或目录\0
。所以我想知道空字符的正确书写方法因为显然它不应该允许我有一个包含 null 的文件名
mkdir '\0'
将创建一个名为\0
还有一个问题,如果我想包含$
在我的文件名中,我可以使用反斜杠
mkdir \$myfile
将创建一个名为 $myfile
但是,如果我用单引号和双引号将美元符号括起来,我也可以这样做
mkdir \$myfile
一样一样mkdir '$'myfile
一样一样mkdir "$"myfile
一样mkdir '$myfile'
一样mkdir "$myfile"
所以我的问题是,单引号和双引号是否可以替代转义反斜杠字符?
$
除了, (空格) 和反斜杠之外,还有哪些字符需要在 bash 中转义?
答案1
打印空字符
$'\0'
在许多最近的 shell 上,您可以使用美元单引号格式、十六进制格式\x00
、unicode 格式\u0000
或 来编写空字符\U00000000
,或者就像您尝试使用八进制:一样'\0'
。关键是该命令必须了解如何处理反斜杠转义字符。例如,在echo
通常情况下需要添加-e
选项,在这种情况下printf
将是%b
。
让我们检查一下它是否有效:
$ echo -ne '\0'
$
所以什么也不产生,就像echo -ne ''
,类似
$ printf '%b' '\0'
$
让我们添加一些字符(printf '%b'
从现在开始我将坚持使用,因为它更强大,但类似的效果是echo -ne
):
$ printf '%b' a'\0'b
ab
只打印了两个字符,无效的去?
$ printf '%b' a'\0'b | wc -c
3
让我们比较一下a''b
:
$ printf '%b' a''b | wc -c
2
最后,在尝试创建文件之前检查我们是否确实打印了空字符,让我们将打印的值传递给将引发错误的命令,例如xargs
:
$ printf '%b' a'\0'b | xargs echo
xargs: Warning: a NUL character occurred in the input. It cannot be
passed through in the argument list. Did you mean to use the --null option?
a
注意a
最后是如何打印 only 的。当然xargs -0
工作得很好:
$ printf '%b' a'\0'b | xargs -0 echo
a b
使用 null 创建文件?
现在让我们尝试创建带有空字符的文件:
$ touch $'\0'
touch: cannot touch ‘’: No such file or directory
$ mkdir $'\0'
mkdir: cannot create directory ‘’: No such file or directory
# let's try another approach - using printf in command substitution:
$ touch "$(printf '%b' '\0')"
touch: cannot touch ‘’: No such file or directory
$ mkdir "$(printf '%b' '\0')"
mkdir: cannot create directory ‘’: No such file or directory
结果和中的一模一样touch ''
,看起来无效的就被一起忽略了。如果我们在命令替换周围跳过双引号会怎么样?
$ touch $(printf '%b' '\0')
touch: missing file operand
Try 'touch --help' for more information.
$ mkdir $(printf '%b' '\0')
mkdir: missing operand
Try 'mkdir --help' for more information.
这与完全没有参数的touch
/的情况相同。mkdir
另一个结果是如果我们用文本包围 null:
$ touch "$(printf '%b' a'\0'b)"
$ ls
a # in zsh
ab # in bash
人们还可以尝试将标准输出重定向到,$'\0'
但得到的只是不同类型的错误。
答案2
单/双引号与反斜杠:单引号和反斜杠的引用能力是相等的。使用单引号来引用带有空格、制表符、换行符()[]*$><?|{}~&;"`^!#
和可能其他我忘记的字符的长字符串要方便得多。但是,您只需使用反斜杠即可获得完全相同的结果(`...`
但要注意反斜杠 ( ) 内反斜杠的过载)
不过,双引号是独一无二的。 $
在双引号内展开,但不在单引号内展开。 “$foo”扩展 foo,但保护扩展结果免受分词和全局扩展的影响。
http://mywiki.wooledge.org/BashFAQ可能是一个很好的起点。 bash 手册并没有花太多时间介绍如何使用它所描述的所有功能,只是介绍它们如何单独工作。
从字面上看,不可能将包含零字节的字符串作为命令行参数传递或传递给系统调用。 ABI(应用程序二进制接口)准确指定数据如何在进程和内核之间传递,使用 C 字符串处理所有内容(二进制数据除外),包括命令行参数和系统调用的文件/路径参数。 C 字符串是字符数组,其中字符串结尾由零字节标记。没有办法“转义”零字节来表明它不是字符串的结尾。
任何尝试做类似的事情touch $'foo\0bar'
只会导致touch
将其参数列表视为
argv[0] = "/bin/touch";
argv[1] = "foo";
尽管在内存中,argv[1] = "foo\0bar\0"
,第一个\0
标记了字符串的结尾。实际上,“foo\0bar\0”不会到达新进程的argv。它不会从exevce(2)
运行的系统调用中的 argv 数组中取出touch
。
即使您编写了包含空字节的字符数组/字符串的 C 或 Perl 程序,将它们传递给系统调用open(2)
也会导致内核对字符串进行相同的解释。需要处理任意数据的系统调用(例如read(2)
和write(2)
)采用长度参数以及指向缓冲区的指针。
使用 bash 甚至不可能对空字节做任何事情。正如 jimmij 指出的那样,使用转义序列处理编写字符串文字的 bash 语法是$'string'
,但\0
在字符串文字中编写 a 充当 bash 中的字符串终止符。我猜这意味着 bash 在内部将字符串存储为 C 字符串,而不是明确的长度。
str=$'foo\0bar'
echo "${#str}" # 3, showing that bash isn't even storing it in a variable.
echo "$str" | wc -c # 4. wouldn't work even if ${#str} did: echo's cmdline would eat it
wc -c <<< $'foo\0bar' # 4 (includes a newline)
所以我们不能使用这种语法在任何地方发送空字节。我们必须使用tr
什么东西。
然而,bashprintf
对\0
.转换%b
处理反斜杠转义,包括\0
.并且printf
已经在其格式字符串中处理了此类转义。
printf '\0'
打印零字节。将其输入hexdump -C
以确认。printf '%s\0%s' foo bar | hexdump -C
写入foo.bar
(其中 . 是 NUL 字节)到 stdout。请注意,\0
单引号或双引号内的内容不会自行扩展;只有$'\0'
引用才能扩展它前printf,这将使其充当终止符。printf '%b' 'foo\0bar'
做同样的事情,但更复杂。
答案3
如您所知,$var
这将导致解释变量。不同选项起作用的原因各不相同:
- escape (
\$var
):不将下一个字符解释为 shell 功能字符。但在某些情况下:赋予特殊含义(例如,\n
在某些情况下用于换行符) - 单引号 (
'$var'
):单引号中的所有内容严格来说都只是它们包含的字符串 $
( )的分隔"$"var
:单个$
不会被解释,通过将其放在双引号中,它与部分分开var
并且不会发生解释- 双引号(
"$var"
):实际上允许解释变量var
:mkdir "$var"
不起作用并且与其他变量不一样!请再检查一次!然而,引号内包含的任何内容都将被视为单个字符串。当文件名中包含特殊字符时特别有用,例如创建名称中包含空格的文件:touch "a b"
->a b
创建/更新单个文件,touch a b
-> 创建/更新两个a
文件b
。
其他特殊运算符有:重定向和“heres” > >> < << <<<
、进程运算符& |
、布尔运算符|| &&
以及命令分隔符;
和用括号分组( )
,有时 - 但随后分隔或作为第一个字符 --
用于标准输入或命令选项。还有我们已经使用过的测试命令[
和引号' "
,以及使用感叹号!
或带哈希的注释来回忆以前的命令,以及用于多个和单个字符的#
通配符星号*
和问号。?
另请注意,当前目录和父目录为.
和..
,而主目录设置为~/
。即,字符; & | > < - [ \ ' " ( ) # * ! ? . ~ ^ { }
、`
、换行符、空格、制表符(以及单字节语言环境中的其他空白字符)应该查看两次,但并非所有这些字符在同一级别上都是“危险的”。我希望我没有忘记,因为有很多。
答案4
在文件名中,'/'
禁止使用,因为它是目录分隔符。这是唯一的原因。如果您手动编辑文件系统,您甚至可以创建一个'/'
名为 的文件(不推荐,因为您将无法用它做太多事情)。
NUL 字符不能用作文件名的一部分,因为相关系统调用使用 C 语言字符串传递约定,而 NUL 是此类字符串的终止符。因此它不能被解释为名称的一部分。
请注意,创建名为 的文件\0
与创建包含 NUL 的文件不同 - 前者是包含两个字符'\'
和 的文件名'0'
。