Ano*_*non 9 command-line bash paths
realpath并readlink返回绝对路径:
+akiva@X230:~$ realpath ZannaIsAwesome
/home/akiva/ZannaIsAwesome
Run Code Online (Sandbox Code Playgroud)
像这样的路径很容易处理。但是,这样的事情会有一些问题:
例如:
因此,需要对这样的名称进行清理,以便能够将其提供给其他命令。用例可能是这样的:
+a@X230:~/\e[92mM@r|< $hu+'|'|_e|\|\|0rth [`-_-"]$ bacon=$(realpath pullingATerdon)
+a@X230:~$ vim $bacon
Run Code Online (Sandbox Code Playgroud)
不用说,vim $bacon不会按预期工作。
我该怎么做才能清理该绝对路径,以便它可以与其他命令一起使用?
ter*_*don 12
首先,始终引用您的变量。如果您正确引用它,您正在尝试做的事情会很好:
$ pwd
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]
$ ls
pullingATerdon
Run Code Online (Sandbox Code Playgroud)
为了保持一致性,我保留了您选择的奇怪文件名(虽然我不知道您为什么选择它)。
现在,让我们将 的路径分配pullingATerdon给一个变量,然后尝试打开该文件:
$ bacon="$(realpath pullingATerdon)"
$ echo "$bacon"
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]/pullingATerdon
$ ls $bacon
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,这失败了。但是,如果我们现在正确引用它:
$ ls -l "$bacon"
-rw-r--r-- 1 terdon terdon 0 Mar 14 23:15 '/home/terdon/foo/\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]/pullingATerdon'
Run Code Online (Sandbox Code Playgroud)
它按预期工作。是的,您也可以在(适当的)编辑器中打开路径:emacs "$bacon"会工作得很好。好的,也会vim和其他任何事情。您选择的编辑器虽然很遗憾,但无关紧要。
跟踪在您的情况下实际发生的情况的一种快速方法是使用set -x(再次使用将其关闭set +x),这会导致 shell 在运行它之前打印它将运行的每个命令。使用以下命令打开 shell 的调试消息set -x:
$ set -x
$ /bin/ls $bacon
+ ls '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':
Run Code Online (Sandbox Code Playgroud)
这向我们展示了ls使用三个单独的参数运行的:'/home/terdon/foo/\e[92mM@r|<','+'\''|'\''|_e|\|\|0rth'和'[`-_-"]/pullingATerdon'。发生这种情况是因为 shell对未加引号的字符串执行分词和全局扩展。在这种情况下,问题是分词,因为 shell 看到路径中的空格并将每个空格分隔的字符串作为单独的参数读取。
该mkdir示例略有不同,但这是因为您向我们展示了第二次调用命令的错误消息。我猜你试过一次,然后第二次运行它以获得你的问题的输出。第一次运行它时,它看起来像这样:
$ mkdir $(realpath pullingATerdon)
++ realpath pullingATerdon
+ mkdir '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
mkdir: cannot create directory ‘[`-_-"]/pullingATerdon’: No such file or directory
Run Code Online (Sandbox Code Playgroud)
同样,由于分词,这将尝试创建三个目录,而不是一个。首先,它创建(成功)目录/home/terdon/foo/\e[92mM@r|<:
$ ls -l /home/terdon/foo/
total 8
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|<'
drwxr-xr-x 3 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]'
Run Code Online (Sandbox Code Playgroud)
然后,它也成功地+'|'|_e|\|\|0rth在当前目录中创建了一个名为的目录:
$ ls -l
total 4
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:37 '+'\''|'\''|_e|\|\|0rth'
-rw-r--r-- 1 terdon terdon 0 Mar 15 00:36 pullingATerdon
Run Code Online (Sandbox Code Playgroud)
然后,它尝试创建目录[`-_-"]/pullingATerdon。这失败了,因为mkdir,默认情况下,不创建子目录(它可以,如果你运行它-p):
$ mkdir baz/bar
mkdir: cannot create directory ‘baz/bar’: No such file or directory
Run Code Online (Sandbox Code Playgroud)
由于您的未加引号的字符串包含一个/,mkdir认为是两个目录的路径,试图找到最上面的一个,但失败了。
这就是它失败的原因,但发生的事情更复杂。您使用的字符串实际上是一个 shell glob,特别是一个glob range,它匹配当前目录中名称为`、-、_或5 个字符之一的所有文件"。由于当前目录中没有此类文件,因此 glob 不匹配任何内容,并且与 bash 中的默认行为一样,返回自身:
$ ls -l
total 4
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:37 '+'\''|'\''|_e|\|\|0rth'
-rw-r--r-- 1 terdon terdon 0 Mar 15 00:36 pullingATerdon
Run Code Online (Sandbox Code Playgroud)
澄清一下,如果你给出一个匹配某些东西的 glob 会发生什么:
$ echo [p]* ## any filename starting with a p
pullingATerdon
$ echo "[p]*" ## the string "[p]*"
[p]*
Run Code Online (Sandbox Code Playgroud)
不带引号的[p*]被扩展为匹配文件名的列表(在这种情况下只有一个),这就是传递给echo. 您应该引用所有内容的另一个原因。
最后,您显示的实际错误来自您第二次运行该命令,并且在尝试 create 时在第一步失败/home/terdon/foo/\e[92mM@r|<,因为之前的调用已经创建了该目录。
更一般地,每当您发现自己使用任意文件名时,请始终使用 shell glob。像这样的事情:
for file in *; do command "$file"; done
Run Code Online (Sandbox Code Playgroud)
这适用于任何文件名。不管它碰巧包含什么。在我们上面的例子中,你可以这样做:
emacs /home/terdon/*92mM*/pullingATerdon
Run Code Online (Sandbox Code Playgroud)
任何唯一标识目标文件的 glob 都可以。这样,您无需担心特殊字符,只需让 shell 处理它们即可。
一些有用的参考资料:
如何查找并安全地处理包含换行符、空格或两者的文件名?:优秀的 Gray Cat's Wiki 上的常见问题解答之一。
忘记在 bash/POSIX shell 中引用变量的安全隐患:我在本答案开头引用的同一篇文章。如果您未能正确引用您的 shell 变量,可能会出错的所有事情的详细解释。
为什么我的 shell 脚本会因空格或其他特殊字符而阻塞?:你想知道的关于在 shell 中处理任意文件名的一切。
什么时候需要双引号?:更多关于引号和变量的信息,特别是一些不需要引用它们的情况