问:有没有简单的sh/bash/zsh/fish/…命令打印任何文件的绝对路径我馈送它?
用例:我在目录/a/b中,我想在命令行上打印文件c的完整路径,这样我就可以轻松地将它粘贴到另一个程序:/a/b/c中。这很简单,但是在处理长路径时,一个小程序可能会为我节省5秒左右的时间。因此,让我感到惊讶的是,我找不到一个标准的实用程序来做这件事——真的没有吗?
下面是一个示例实现,abspath.py:
#!/usr/bin/python
# Author: Diggory Hardy <diggory.hardy@gmail.com>
# Licence: public domain
# Purpose: print the absolute path of all input paths
import sys
import os.path
if len(sys.argv)>1:
for i in range(1,len(sys.argv)):
print os.path.abspath( sys.argv[i] )
sys.exit(0)
else:
print >> sys.stderr, "Usage: ",sys.argv[0]," PATH."
sys.exit(1)
在某些情况下,这个问题最上面的答案可能具有误导性。假设你要查找的文件的绝对路径在$ path变量中:
# node is in $PATH variable
type -P node
# /home/user/.asdf/shims/node
cd /tmp
touch node # But because there is a file with the same name inside the current dir check out what happens below
readlink -e node
# /tmp/node
readlink -m node
# /tmp/node
readlink -f node
# /tmp/node
echo "$(cd "$(dirname "node")"; pwd -P)/$(basename "node")"
# /tmp/node
realpath node
# /tmp/node
realpath -e node
# /tmp/node
# Now let's say that for some reason node does not exist in current directory
rm node
readlink -e node
# <nothing printed>
readlink -m node
# /tmp/node # Note: /tmp/node does not exist, but is printed
readlink -f node
# /tmp/node # Note: /tmp/node does not exist, but is printed
echo "$(cd "$(dirname "node")"; pwd -P)/$(basename "node")"
# /tmp/node # Note: /tmp/node does not exist, but is printed
realpath node
# /tmp/node # Note: /tmp/node does not exist, but is printed
realpath -e node
# realpath: node: No such file or directory
基于以上,我可以得出结论:realpath -e和readlink -e可以用于查找文件的绝对路径,我们期望存在于当前目录中,而不受$ path变量的影响。唯一的区别是realpath输出到stderr,但如果没有找到file,两者都会返回错误代码:
cd /tmp
rm node
realpath -e node ; echo $?
# realpath: node: No such file or directory
# 1
readlink -e node ; echo $?
# 1
现在,如果您想要一个存在于$ path中的文件的绝对路径a,那么下面的命令将是合适的,这与当前目录中是否存在同名文件无关。
type -P example.txt
# /path/to/example.txt
# Or if you want to follow links
readlink -e $(type -P example.txt)
# /originalpath/to/example.txt
# If the file you are looking for is an executable (and wrap again through `readlink -e` for following links )
which executablefile
# /opt/bin/executablefile
如果没有,回退到$PATH,示例:
cd /tmp
touch node
echo $(readlink -e node || type -P node)
# /tmp/node
rm node
echo $(readlink -e node || type -P node)
# /home/user/.asdf/shims/node
Alexander Klimetschek的答案是可以的,如果您的脚本可能坚持使用bash或bash兼容的shell。它不能与只符合POSIX的shell一起工作。
此外,当最终文件是根目录下的文件时,输出将是//file,这在技术上并不是不正确的(系统将双/视为单/),但它看起来很奇怪。
下面是一个适用于所有符合POSIX标准的shell的版本,它所使用的所有外部工具也是POSIX标准所要求的,并且它显式地处理根文件的情况:
#!/bin/sh
abspath ( ) {
if [ ! -e "$1" ]; then
return 1
fi
file=""
dir="$1"
if [ ! -d "$dir" ]; then
file=$(basename "$dir")
dir=$(dirname "$dir")
fi
case "$dir" in
/*) ;;
*) dir="$(pwd)/$dir"
esac
result=$(cd "$dir" && pwd)
if [ -n "$file" ]; then
case "$result" in
*/) ;;
*) result="$result/"
esac
result="$result$file"
fi
printf "%s\n" "$result"
}
abspath "$1"
将其放入一个文件并使其可执行,您就有了一个CLI工具来快速获取文件和目录的绝对路径。或者只是复制该函数并在您自己的POSIX符合脚本中使用它。它将相对路径转换为绝对路径并返回绝对路径。
有趣的修改:
如果将result=$(cd "$dir" && pwd)替换为result=$(cd "$dir" && pwd -P),那么最终文件路径中的所有符号链接也将被解析。
如果你对第一个修改不感兴趣,你可以通过提前返回来优化绝对情况:
abspath ( ) {
if [ ! -e "$1" ]; then
return 1
fi
case "$1" in
/*)
printf "%s\n" "$1"
return 0
esac
file=""
dir="$1"
if [ ! -d "$dir" ]; then
file=$(basename "$dir")
dir=$(dirname "$dir")
fi
result=$(cd "$dir" && pwd)
if [ -n "$file" ]; then
case "$result" in
*/) ;;
*) result="$result/"
esac
result="$result$file"
fi
printf "%s\n" "$result"
}
既然问题出现了:为什么printf而不是echo?
Echo主要用于将消息打印给用户到标准输出。脚本编写者所依赖的许多回显行为实际上是未指定的。甚至著名的-n也没有标准化,tab的\t用法也没有标准化。POSIX标准说:
要写入标准输出的字符串。如果第一个操作数是-n,或者任何一个操作数包含一个字符,则结果是由实现定义的。
——https://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html
因此,每当您想要向stdout写入一些内容,而不是为了将消息打印给用户时,建议使用printf,因为printf的行为是精确定义的。我的函数使用stdout传递一个结果,这不是给用户的消息,因此只使用printf保证了完美的可移植性。