【问题标题】:get parent directory of a file in bash在bash中获取文件的父目录
【发布时间】:2017-04-03 15:56:55
【问题描述】:

如何获取文件的父目录?

我希望它在所有类型的名称上都是安全的:

.
..
path/to/my/file
/absolute/path/to/my/file
'-rf --no-preserve-root whatever'/test.zip
(symbolic links)
`'"`'{(})

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

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

[1]bash get the parent directory of current directory

[2]Retrieve parent directory of script

[3]bash filepath to parent directory of file

【问题讨论】:

  • 您是在查找包含文件的目录的完整路径名,还是包含该文件的目录的父目录?您是否对遍历文件名中所述的路径或获取文件系统上的规范位置更感兴趣?
  • 两个问题的第二种情况。
  • 假设您的当前目录是/home/user/bin,“文件名”是..(父当前目录)。据推测,包含该条目的目录是/home/user/bin,因此父目录是/home/user,或者是否需要一些其他解释(目录../home/user,所以父目录可能是/home)。我认为“假定”变体是正确的,与. 类似,但也许也可以检查一下。
  • -rf…/test.zip 名称的认可解释是什么?那是一个包含空格、破折号等的目录名称吗?另外,如果文件名不存在怎么办?这是一个错误,还是代码应该对文件名进行文本分析以尝试推断如果文件存在,文件名是什么?
  • @JonathanLeffler 至于“如果文件不存在怎么办” - 老实说,我不知道。 SO 问题往往会超出原作者的意图,至少对于有价值的问题是这样。所以我的解释和你的可能会有所不同。我个人知道存在一个文件,这些情况对我来说是无法区分的。

标签: linux bash shell sh


【解决方案1】:

真正安全的解决方案:

parent_dir="$(dirname -- "$(realpath -- "$file_name")")"

如果您的系统没有realpath,但有readlink,这应该可以:

parent_dir="$(dirname -- "$(readlink -f -- "$file_name")")"

【讨论】:

  • 这是一个 bash 问题。我在 OS X、FreeBSD 和其他非 Linux 操作系统上使用 bash,这些操作系统没有您建议的 realpath 命令,这不是 bash 的一部分。哎呀,即使在我管理的 Linux 系统上,realpath 也是可用的,但默认情况下没有安装。如果您确实觉得有必要推荐仅适用于某个特定平台的内容,请至少提及该平台。
  • @ghoti FreeBSD 有一个realpath(1) 实用程序。 “realpath 实用程序首次出现在 FreeBSD 4.3 中。”
  • @kdhp:尽管 macOS Sierra(和之前的 Mac OS X)是从 BSD 派生的,但默认情况下它们不包含 realpath 命令。在我的机器上,我有命令的实现和命令的 GNU 实现。而且,鉴于realpath 不是标准化的(例如通过 POSIX),FreeBSD 和 GNU 实现不太可能完全兼容。不过,幸运的是,它们具有共同的功能子集。 macOS 上有一个 readlink 命令,但它的选项比 GNU 的版本少得多(它有 -n 选项,两者似乎相同)。
  • 与单纯使用dirname …相比,printf '%s\n' "$(dirname …)"有什么好处?
  • @kdhp:这并不是真正的争论,而是仅供参考……有趣的历史!此外,macOS readlink 恰好有一个选项(-n 表示没有换行符); GNU readlink 有 8 个选项(每个选项都有一个长名称和一个短单字符名称)加上 --help--version。其中,-n (--no-newline) 选项在两者之间是相同的。我不确定这 7 个选项是否是您提到的“遗留边缘案例”。我同意realpath 命令的核心功能是基于realpath 函数。也许我会在电影结束后删除它……这并不重要。
【解决方案2】:

Bash 的 cd 命令有几个有趣但很少使用的选项,-P-L

   cd [-L|[-P [-e]] [-@]] [dir]
      ...    The  -P  option  causes  cd to use the physical directory
      structure by resolving symbolic links while traversing  dir  and
      before processing instances of .. in dir (see also the -P option
      to the set builtin command); the -L option forces symbolic links
      to  be followed by resolving the link after processing instances
      of .. in dir. ...

所以...如果您正在寻找当前工作目录的文件系统中的物理位置,您可以使用以下内容:

realwd="$(cd -P .; pwd)"

在您的 cmets 中,您提到您正在寻找包含文件的 目录 的父目录 -- 因此,如果路径是 /foo/bar/baz/filename,您将寻找 @ 987654329@.

为此,我建议结合使用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-Pnot bash specific
  • @kdhp - 是的,但它们也不是通用的——仍然有 tcsh 用户。我提到它们是 bash 选项,因为 OP 标记了他的问题bash。我的答案中的函数应该在任何 POSIX shell 中都可以使用 .. 也可以在 bash 中使用。 :)
猜你喜欢
  • 2012-01-15
  • 2014-02-11
  • 2011-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多