在bash中获取文件的父目录
在bash中获取文件的父目录
如何获取文件的父目录?
我希望能够在各种名称上都安全使用:
. .. path/to/my/file /absolute/path/to/my/file '-rf --no-preserve-root whatever'/test.zip (符号链接) `'"`'{(})
我对获取文件系统上的规范位置更感兴趣,而不是遍历文件名中指定的路径。
请注意,有类似的问题,但没有一个专注于正确性、相对/绝对路径和“不安全”名称:
[1] bash 获取当前目录的父目录
[2] 获取脚本的父目录
[3] bash 获取文件的父目录
在bash中获取文件的父目录
在bash中,有时候我们需要获取一个文件的父目录。下面是一些在bash中获取父目录的方法:
获取当前目录的父目录:
parent_dir="$(dirname -- "$(realpath -- "$PWD")")"
获取正在运行脚本的目录:
parent_dir="$(dirname -- "$(realpath -- "$0")")"
获取任何文件的父目录:
parent_dir="$(dirname -- "$(realpath -- "$file_or_dir_name")")"
如果你的系统没有`realpath`命令,但有`readlink`命令,可以使用以下方法:
parent_dir="$(dirname -- "$(readlink -f -- "$file_name")")"
这是一个关于bash的问题。我在OS X、FreeBSD等不是Linux的操作系统上使用bash,它们没有你提到的`realpath`命令,该命令也不是bash的一部分。即使在我管理的Linux系统上,`realpath`也是可用但不默认安装的。如果你确实需要提出只适用于特定平台的建议,请至少提到该平台。
FreeBSD有一个`realpath(1)`实用程序。"realpath实用程序首次出现在FreeBSD 4.3中。"
尽管macOS Sierra(以及之前的Mac OS X)是基于BSD的,但它们默认不包含`realpath`命令。在我的机器上,我有自己的命令实现和GNU的命令实现。鉴于`realpath`没有标准化(例如通过POSIX),FreeBSD和GNU的实现可能不完全兼容。但希望它们具有共同的功能子集。macOS上有一个`readlink`命令,但它的选项比GNU版本少得多(它有`-n`选项,在两者上似乎是相同的)。
`printf '%s\n' "$(dirname …)"`与简单地使用`dirname …`相比有什么好处?
好问题!在我的实际脚本中,我需要一个变量,所以代码是`parent_dir="$(...)"`。我想我应该使用一个变量或避免`printf`层。会这么做。再次感谢!
FreeBSD的`realpath(1)`实用程序可以追溯到2002年(在最初的OS X发布之后),在FreeBSD及其分支之外找不到。FreeBSD和GNU的`realpath(1)`应该是兼容的,除了选项,因为`realpath(3)`函数是标准化的。然而,`readlink(1)`将在任何GNU或BSD用户空间中预设置(除了一些遗留边缘情况)。
哦,天哪,你是对的。我之前肯定是打错了。感谢你指出来。尽管如此,它并不是bash的一部分。
这不是一个真正的论点,而是一个FYI...有趣的历史!此外,macOS的`readlink`只有一个选项(`-n`用于取消换行符);GNU的`readlink`有8个选项(每个选项都有一个长名称和一个短的单字符名称),另外还有`--help`和`--version`。其中,这7个选项是否是你提到的“遗留边缘情况”,我不确定。我同意`realpath`命令的核心功能是基于`realpath`函数的。也许我会在电影之后删除这个...它并不是非常重要。
你是对的,`-f`选项相当新(2010年),macOS(OS X)倾向于使用陈旧的BSD实用程序。至于边缘情况,我指的是存在`realpath(1)`而不存在`readlink(1)`的情况。
在bash中,使用cd
命令有一些很有趣但很少使用的选项,-P
和-L
。
cd [-L|[-P [-e]] [-@]] [dir] ... -P选项使cd在遍历dir时使用物理目录结构,解析符号链接,并在处理dir中的..实例之前;-L选项强制跟踪符号链接,在处理dir中的..实例之后解析链接。...
所以...如果你正在寻找当前工作目录在文件系统中的物理位置,你可以使用类似这样的命令:
realwd="$(cd -P .; pwd)"
在你的评论中,你提到你正在寻找包含文件的目录的父目录 -- 所以,如果路径是/foo/bar/baz/filename
,你应该寻找/foo/bar
。
为了实现这一点,我建议结合使用cd -P
和参数扩展。由于你知道/
字符永远不会作为文件名的一部分存在,下面的方法可能适合你:
grandparent() { local realdir="$(cd -P "${1%/*}"; pwd)" echo "${realdir%/*}" }
这个方法使用cd -P
来获取文件的物理位置,然后使用参数扩展来删除路径中的最后一个项目。
$ mkdir -p one/two/three $ touch one/two/three/foo $ ln -s one/two/three bar $ ls -l bar lrwxr-xr-x 1 ghoti wheel 13 Nov 19 23:05 bar -> one/two/three $ grandparent bar/foo /usr/home/ghoti/tmp6/one/two
-L
和-P
不是