在bash中获取文件的父目录

13 浏览
0 Comments

在bash中获取文件的父目录

如何获取文件的父目录?

我希望能够在各种名称上都安全使用:

.
..
path/to/my/file
/absolute/path/to/my/file
'-rf --no-preserve-root whatever'/test.zip
(符号链接)
`'"`'{(})

我对获取文件系统上的规范位置更感兴趣,而不是遍历文件名中指定的路径。

请注意,有类似的问题,但没有一个专注于正确性、相对/绝对路径和“不安全”名称:

[1] bash 获取当前目录的父目录

[2] 获取脚本的父目录

[3] bash 获取文件的父目录

0
0 Comments

在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)`的情况。

0
0 Comments

在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不是特定的。-没错,但它们也不是通用的——仍然有使用tcsh的用户。我之所以提到它们是bash选项,是因为OP标记了他的问题为bash。我的答案中的函数应该在任何POSIX shell中都可以工作...也可以在bash中工作。:)

0