【问题标题】:Is there a way to include the ~ in the output of realpath有没有办法将 ~ 包含在 realpath 的输出中
【发布时间】:2016-01-06 20:32:49
【问题描述】:

realpath 非常好,因为它为您提供了一种获取文件真实路径的简单方法,但我无法找到满足我需求的方法(不进行自己的字符串操作,因此可能缺少一个专门的实用程序可能已经找到并解决的极端情况)。简而言之,我想在它返回的路径中包含~,这样它就可以跨机器有效。

因此,如果我在 ~/bin 中输入 realpath --some-option script.sh,我希望它返回 ~/bin/script.sh 而不是 bin/script.sh,如果我使用的选项是 --relative-base=$HOME,而不是/Users/brandon/bin/script.sh,如果我不使用任何选项,我会得到。

有没有什么方法可以从realpath 得到我想要的东西,或者有没有其他实用程序可以简单地完成我想要的东西?或者是通过sed 获得我想要的唯一真正的选择?

(附注:我在 OS X 上,但我已经安装了 GNU 实用程序,以备不时之需……我不确定这是 realpath 的来源还是现在默认安装在更新的 OS X 版本。)

【问题讨论】:

  • 一般来说,拥有~ 对人类展示以外​​的其他目的是有害的——shell 只在少数情况下进行波浪号扩展,open() 等不会兑现它完全没有。
  • 通过 sed 进行管道传输显然并不难,但似乎 应该 已经内置了一些我忽略的东西......否则它的 realpath ../some/path/ | sed -e "s|$HOME|~|"
  • @CharlesDuffy:我将编写使用路径的代码,所以我可以确保它可以处理~ 好的。但您的观点可能是没有其他人认为适合将此选项添加到 realpath 的原因。
  • 假设您要捕获到一个 shell 变量,当然可以执行类似 [[ $path = "$HOME"/* ]] && path="~/"${path#"$HOME/"} 的操作,而开销比启动诸如 sed 之类的外部命令要少(而且错误更少——没有机会主目录名称中的字符被视为正则表达式内容而不是文字)。

标签: shell sed realpath


【解决方案1】:

即使是当前版本的 OSX (10.11.3)没有带有 realpath 实用程序,所以你的必须确实来自 GNU coreutils Homebrew 包。

实际上,您可以通过realpath 本身轻松验证;例如:

$ realpath "$(which realpath)"
/usr/local/Cellar/coreutils/8.24/bin/grealpath

使用--relative-base="$HOME" 并简单地在~/ 前面加上Bujiraso's helpful answer 是您情况下最简单的解决方案。

让我提出一个替代方案:

  • 不用realpath 也行,因为不能假定它在 OSX 机器上存在。

    • 改为使用 Perl 单线(Python 也是一种选择)。
  • 使用$HOME 而不是~,因为基于$HOME 的路径是更安全 的选择(假设shell 扩展了路径):

    • $HOME 更安全,因为它允许 整个 路径字符串在传递给 shell 时被双引号括起来,而不必担心单独引用(转义)嵌入的空格和其他 shell 元字符;相比之下,使用 ~ 前缀的路径,您要么必须在路径中引用 individual 个字符,要么选择性地保留 ~/ 前缀 未引用 (~/"path/..." )。
absPath=$(perl -MCwd -le 'print Cwd::abs_path(shift)' "script.sh")
abstractPath="\$HOME${absPath#"$HOME"}"

结果将类似于 literal $HOME/bin/script.sh,当被 shell 解释时,会扩展为当前用户特定的路径。

如果您定义如下所示的abstractHomePath() bash 函数,则可以使用:

abstractPath=$(abstractHomePath "script.sh")  # -> '$HOME/bin/script.sh'

abstractHomePath Bash 函数:

# Given a relative or absolute path, returns an absolute path in which the path
# prefix that is the current user's home directory is replaced with literal
# '$HOME' so as to yield an abstract path that can later be expanded in a 
# different user's context to get the analogous path. 
#
# If the input path resolves to an absolute path that is *not* prefixed with
# the current user's home diretory path, the absolute path is returned as -is.
#
# Note that any symlinks in the path are resolved to their ultimate targets
# first and that the path is normalized.
#
# Examples:
#    abstractHomePath ~ # -> '$HOME'
#    abstractHomePath /Users/jdoe/Downloads # -> '$HOME/Downloads'
#    abstractHomePath /tmp # -> '/private/tmp' -  symlink resolved, but no $HOME prefix
# Prerequisites:
#    Requires Perl v5+
abstractHomePath() {
  local absPath=$(perl -MCwd -le 'print Cwd::abs_path(shift)' "$1")
  if [[ $absPath == "$HOME"* ]]; then
    printf '%s\n' "\$HOME${absPath#"$HOME"}"
  else
    printf '%s\n' "$absPath"
  fi
}

【讨论】:

    【解决方案2】:

    realpath 本身似乎没有此功能,但是一个简单的解决方案可能是回显波浪号+斜杠本身的前缀,然后是相对实路径。也就是说,您可以使用 relative-base 和 echo 的组合,而不是 sed。 例如:

    [user@localhost ~]$ echo "~/$(realpath --relative-base=$HOME bin/myScript)"
    ~/bin/myScript 
    

    【讨论】:

      猜你喜欢
      • 2021-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-06
      相关资源
      最近更新 更多