是否有一个命令来检索给定相对路径的绝对路径?
例如,我想要$line包含dir ./etc/中每个文件的绝对路径
find ./ -type f | while read line; do
echo $line
done
是否有一个命令来检索给定相对路径的绝对路径?
例如,我想要$line包含dir ./etc/中每个文件的绝对路径
find ./ -type f | while read line; do
echo $line
done
在find的情况下,可能最简单的是给它搜索的绝对路径,例如:
find /etc
find `pwd`/subdir_of_current_dir/ -type f
不管怎样,我投票给了被选中的答案,但我想分享一个解决方案。缺点是,它只适用于Linux——在出现堆栈溢出之前,我花了大约5分钟的时间试图找到OSX的等效版本。但我相信它就在那里。
在Linux上,可以将readlink -e与dirname结合使用。
$(dirname $(readlink -e ../../../../etc/passwd))
收益率
/etc/
然后你用dirname的姐妹,basename 文件名
$(basename ../../../../../passwd)
收益率
passwd
把它们放在一起…
F=../../../../../etc/passwd
echo "$(dirname $(readlink -e $F))/$(basename $F)"
收益率
/etc/passwd
如果目标目录是安全的,basename将不会返回任何内容 在最终输出中,你只会得到双斜杠。
realpath试试。
~ $ sudo apt-get install realpath # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc
要避免扩展符号链接,请使用realpath -s。
答案来自“bash/fish命令打印文件的绝对路径”。
Realpath可能是最好的
但是…
最初的问题一开始就很混乱,没有一个很好的例子 与刚才提到的问题有关。
所选的答案实际上回答了给出的例子,但根本不是 题目中的问题。第一个命令是答案(is it 真的吗?我怀疑),没有'/'也可以。我看不见 第二个命令在做什么。
有几个问题是复杂的:
changing a relative pathname into an absolute one, whatever it denotes, possibly nothing. (Typically, if you issue a command such as touch foo/bar, the pathname foo/bar must exist for you, and possibly be used in computation, before the file is actually created.) there may be several absolute pathname that denote the same file (or potential file), notably because of symbolic links (symlinks) on the path, but possibly for other reasons (a device may be mounted twice as read-only). One may or may not want to resolve explicity such symlinks. getting to the end of a chain of symbolic links to a non-symlink file or name. This may or may not yield an absolute path name, depending on how it is done. And one may, or may not want to resolve it into an absolute pathname.
没有选项的命令readlink foo只在其 参数foo是一个符号链接,答案就是它的值 符号链接。后面没有其他链接。答案可能是相对路径: 无论符号链接参数的值是什么。
但是,readlink具有适用于所有对象的选项(-f -e或-m) 文件,并给出一个绝对路径名(没有符号链接的路径名) 由参数实际表示的文件。
这适用于任何不是符号链接的内容,尽管可能存在这种情况 希望使用绝对路径名而不解析中间路径 路径上的符号链接。这是通过命令realpath -s foo完成的
在符号链接参数的情况下,readlink及其选项将 再次解析参数的绝对路径上的所有符号链接,但是 这也将包括可能遇到的所有符号链接 在参数值之后。如果你想要一个 参数符号链接本身的绝对路径,而不是指向任何东西 它可能链接到。同样,如果foo是符号链接,realpath -s foo将 获取一个绝对路径,而不解析符号链接,包括符号链接 给出作为论据的。
如果没有-s选项,realpath的作用与 Readlink,除了简单地读取一个链接以及几个链接的值 其他的事情。我只是不清楚为什么readlink有它的 选项,显然造成了不受欢迎的冗余 realpath。
探索网络并不能说明更多,除了可能会有 跨系统的一些变化。
结论:realpath是最适合使用的命令 灵活性,至少对于这里要求的用途。
我认为这是最便携的:
abspath() {
cd "$(dirname "$1")"
printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
cd "$OLDPWD"
}
但是,如果路径不存在,它将失败。
如果你在Mac OS X上使用bash,它既没有realpath也没有readlink可以打印绝对路径,你可以选择编写自己的版本来打印它。 这是我的实现:
(纯bash)
abspath(){
local thePath
if [[ ! "$1" =~ ^/ ]];then
thePath="$PWD/$1"
else
thePath="$1"
fi
echo "$thePath"|(
IFS=/
read -a parr
declare -a outp
for i in "${parr[@]}";do
case "$i" in
''|.) continue ;;
..)
len=${#outp[@]}
if ((len==0));then
continue
else
unset outp[$((len-1))]
fi
;;
*)
len=${#outp[@]}
outp[$len]="$i"
;;
esac
done
echo /"${outp[*]}"
)
}
(用呆呆的)
abspath_gawk() {
if [[ -n "$1" ]];then
echo $1|gawk '{
if(substr($0,1,1) != "/"){
path = ENVIRON["PWD"]"/"$0
} else path = $0
split(path, a, "/")
n = asorti(a, b,"@ind_num_asc")
for(i in a){
if(a[i]=="" || a[i]=="."){
delete a[i]
}
}
n = asorti(a, b, "@ind_num_asc")
m = 0
while(m!=n){
m = n
for(i=1;i<=n;i++){
if(a[b[i]]==".."){
if(b[i-1] in a){
delete a[b[i-1]]
delete a[b[i]]
n = asorti(a, b, "@ind_num_asc")
break
} else exit 1
}
}
}
n = asorti(a, b, "@ind_num_asc")
if(n==0){
printf "/"
} else {
for(i=1;i<=n;i++){
printf "/"a[b[i]]
}
}
}'
fi
}
(纯BSD AWK)
#!/usr/bin/env awk -f
function abspath(path, i,j,n,a,b,back,out){
if(substr(path,1,1) != "/"){
path = ENVIRON["PWD"]"/"path
}
split(path, a, "/")
n = length(a)
for(i=1;i<=n;i++){
if(a[i]==""||a[i]=="."){
continue
}
a[++j]=a[i]
}
for(i=j+1;i<=n;i++){
delete a[i]
}
j=0
for(i=length(a);i>=1;i--){
if(back==0){
if(a[i]==".."){
back++
continue
} else {
b[++j]=a[i]
}
} else {
if(a[i]==".."){
back++
continue
} else {
back--
continue
}
}
}
if(length(b)==0){
return "/"
} else {
for(i=length(b);i>=1;i--){
out=out"/"b[i]
}
return out
}
}
BEGIN{
if(ARGC>1){
for(k=1;k<ARGC;k++){
print abspath(ARGV[k])
}
exit
}
}
{
print abspath($0)
}
例子:
$ abspath I/am/.//..//the/./god/../of///.././war
/Users/leon/I/the/war
类似于@ernest-a的答案,但不影响$OLDPWD或定义一个新函数,您可以触发一个子shell (cd <path>;pwd)
$ pwd
/etc/apache2
$ cd ../cups
$ cd -
/etc/apache2
$ (cd ~/..; pwd)
/Users
$ cd -
/etc/cups
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
一些解释
这个脚本获取相对路径作为参数"$1" 然后我们得到该路径的dirname部分(你可以将dir或file传递给这个脚本): 然后我们将“$(dirname“$1”)cd到这个相对目录中,并通过运行pwd shell命令获得它的绝对路径 在此之后,我们将basename附加到绝对路径: 作为最后一步,我们重复它
如果相对路径是一个目录路径,那么试试我的,应该是最好的:
absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)
echo $absPath
echo "mydir/doc/ mydir/usoe ./mydir/usm" | awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'
如果你想将一个包含相对路径的变量转换为绝对路径,这是可行的:
dir=`cd "$dir"`
"cd"在不改变工作目录的情况下被回显,因为它是在子shell中执行的。
欧根的回答对我来说不太管用,但这个却管用:
absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"
旁注,当前工作目录不受影响。
我最喜欢的解决方案是@EugenKonkov的解决方案,因为它没有暗示其他实用程序的存在(coreutils包)。
但是对于相对路径“.”和“..”它失败了,所以这里有一个稍微改进的版本来处理这些特殊情况。
但是,如果用户没有cd到相对路径的父目录的权限,它仍然会失败。
#! /bin/sh
# Takes a path argument and returns it as an absolute path.
# No-op if the path is already absolute.
function to-abs-path {
local target="$1"
if [ "$target" == "." ]; then
echo "$(pwd)"
elif [ "$target" == ".." ]; then
echo "$(dirname "$(pwd)")"
else
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
fi
}
恕我直言,最好的解决方案是在这里发布的:https://stackoverflow.com/a/3373298/9724628。
它确实需要python才能工作,但它似乎涵盖了所有或大部分边缘情况,是非常可移植的解决方案。
通过解析符号链接:
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
或者没有它:
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
这是来自所有其他解决方案的链式解决方案,例如,当realpath失败时,要么是因为没有安装它,要么是因为它带着错误代码退出,那么,将尝试下一个解决方案,直到它获得正确的路径。
#!/bin/bash
function getabsolutepath() {
local target;
local changedir;
local basedir;
local firstattempt;
target="${1}";
if [ "$target" == "." ];
then
printf "%s" "$(pwd)";
elif [ "$target" == ".." ];
then
printf "%s" "$(dirname "$(pwd)")";
else
changedir="$(dirname "${target}")" && basedir="$(basename "${target}")" && firstattempt="$(cd "${changedir}" && pwd)" && printf "%s/%s" "${firstattempt}" "${basedir}" && return 0;
firstattempt="$(readlink -f "${target}")" && printf "%s" "${firstattempt}" && return 0;
firstattempt="$(realpath "${target}")" && printf "%s" "${firstattempt}" && return 0;
# If everything fails... TRHOW PYTHON ON IT!!!
local fullpath;
local pythoninterpreter;
local pythonexecutables;
local pythonlocations;
pythoninterpreter="python";
declare -a pythonlocations=("/usr/bin" "/bin");
declare -a pythonexecutables=("python" "python2" "python3");
for path in "${pythonlocations[@]}";
do
for executable in "${pythonexecutables[@]}";
do
fullpath="${path}/${executable}";
if [[ -f "${fullpath}" ]];
then
# printf "Found ${fullpath}\\n";
pythoninterpreter="${fullpath}";
break;
fi;
done;
if [[ "${pythoninterpreter}" != "python" ]];
then
# printf "Breaking... ${pythoninterpreter}\\n"
break;
fi;
done;
firstattempt="$(${pythoninterpreter} -c "import os, sys; print( os.path.abspath( sys.argv[1] ) );" "${target}")" && printf "%s" "${firstattempt}" && return 0;
# printf "Error: Could not determine the absolute path!\\n";
return 1;
fi
}
printf "\\nResults:\\n%s\\nExit: %s\\n" "$(getabsolutepath "./asdfasdf/ asdfasdf")" "${?}"
对@ernest-a相当不错的版本的改进:
absolute_path() {
cd "$(dirname "$1")"
case $(basename $1) in
..) echo "$(dirname $(pwd))";;
.) echo "$(pwd)";;
*) echo "$(pwd)/$(basename $1)";;
esac
}
这正确地处理了路径的最后一个元素是..,在这种情况下,@ernest-a的答案中的“$(pwd)/$(basename“$1”)”将通过作为accurate_sub_path/spurious_subdirectory/…
你可以使用bash字符串替换任何相对路径$line:
line=$(echo ${line/#..\//`cd ..; pwd`\/})
line=$(echo ${line/#.\//`pwd`\/})
echo $line
基本的字符串前端替换遵循以下公式 ${/ #子字符串替换字符串} 在这里讨论得很好:https://www.tldp.org/LDP/abs/html/string-manipulation.html
当我们希望/是我们找到/替换的字符串的一部分时,\字符对/求反。
我无法找到一个解决方案,可以在Mac OS Catalina, Ubuntu 16和Centos 7之间巧妙地移植,所以我决定用python内联来做,它对我的bash脚本工作得很好。
to_abs_path() {
python -c "import os; print os.path.abspath('$1')"
}
to_abs_path "/some_path/../secrets"
下面是一个相当短的函数,可以用来完全绝对化和规范化任何给定的输入路径,只使用POSIX shell和readlink语义:
canonicalize_path() {
(
FILEPATH="$1"
for _ in 1 2 3 4 5 6 7 8; # Maximum symlink recursion depth
do
cd -L "`case "${FILEPATH}" in */*) echo "${FILEPATH%/*}";; *) echo ".";; esac`/" # cd $(dirname)
if ! FILEPATH="$(readlink "${FILEPATH##*/}" || ( echo "${FILEPATH##*/}" && false ) )";
then
break
fi
done
cd -P "." || return $?
echo "$(pwd)/${FILEPATH}"
)
}
如果引用的文件不存在,则只解析指向最终文件名的目录路径。如果指向该文件路径的任何目录都不存在,将返回一个cd错误。这恰好是它试图模拟的GNU/Linux readlink -f命令的确切语义。
在bash/zsh中,您还可以将数字列表压缩为{1..8}或类似的。之所以选择8这个数字,是因为这是Linux多年来的最大限制,后来在4.2版本中将整个路径的分辨率限制为40。如果达到了分辨率限制,代码不会失败,而是返回最后考虑的路径——但是可以添加显式的[- l "${FILEPATH}"]检查来检测这种情况。
这段代码也可以很容易地重用,以确保当前工作目录与已执行脚本在文件系统中的位置匹配(shell脚本的常见需求),只需删除函数和子shell包装器:
FILEPATH="$0"
for _ in 1 2 3 4 5 6 7 8; # Maximum symlink recursion depth
do
cd -L "`case "${FILEPATH}" in */*) echo "${FILEPATH%/*}";; *) echo ".";; esac`/" # cd $(dirname)
if ! FILEPATH="$(readlink "${FILEPATH##*/}" || ( echo "${FILEPATH##*/}" && false ) )";
then
break
fi
done
cd -P "."
FILEPATH="$(pwd)/${FILEPATH}"
我发现Eugen Konkov的答案是最好的,因为它不需要安装任何程序。但是,对于不存在的目录,它将失败。
我写了一个函数,适用于不存在的目录:
function getRealPath()
{
local -i traversals=0
currentDir="$1"
basename=''
while :; do
[[ "$currentDir" == '.' ]] && { echo "$1"; return 1; }
[[ $traversals -eq 0 ]] && pwd=$(cd "$currentDir" 2>&1 && pwd) && { echo "$pwd/$basename"; return 0; }
currentBasename="$(basename "$currentDir")"
currentDir="$(dirname "$currentDir")"
[[ "$currentBasename" == '..' ]] && (( ++traversals )) || { [[ traversals -gt 0 ]] && (( traversals-- )) || basename="$currentBasename/$basename"; }
done
}
它通过使用dirname向上遍历直到cd成功,然后返回当前目录加上dirname删除的所有内容来解决不存在目录的问题。
基于@EugenKonkov的回答和@HashChange的回答,我的回答结合了前者的简洁和对。和. .关于后者。我相信下面所有的选项仅仅依赖于基本的Shell命令语言POSIX标准。
使用dirname和basename,一个选项是:
absPathDirname()
{
[ -d "${1}" ] && set -- "${1}" || set -- "`dirname "${1}"`" "/`basename "${1}"`"
echo "`cd "${1}"; pwd`${2}";
}
不使用dirname或basename,另一个简单的选项是:
absPathMinusD()
{
[ -d "${1}" ] && set -- "${1}" || set -- "${1%${1##*/}}" "/${1##*/}"
echo "`cd "${1:-.}"; pwd`${2}";
}
我会推荐上面两个选择中的一个,其他的只是为了好玩…
Grep版本:
absPathGrep()
{
echo "`[ "${1##/*}" ] && echo "$1" | grep -Eo '^(.*/)?\.\.($|/)' | { read d && cd "$d"; echo "${PWD}/${1#$d}"; } || echo "$1"`"
}
作为一个有趣的“可以用shell有限的RegEx做什么”的例子:
absPathShellReplace()
{
E="${1##*/}"; D="${E#$E${E#.}}"; DD="${D#$D${D#..}}"
DIR="${1%$E}${E#$DD}"; FILE="${1#$DIR}"; SEP=${FILE:+/}
echo "`cd "${DIR:-.}"; pwd`${SEP#$DIR}$FILE"
}
蓝光:
cd $relative_path ; pwd
这里有一个POSIX兼容的解释(我认为),所以它应该在任何平台上工作。
当然,这是可编写脚本的,但我认为分解它可能会使一些人更容易理解/修改特定的用例。
你可以使用which, locate, find,全路径等等。
x = your_file_name
$ x="nvi"
文件容易指示符号链接
$ file -h `which $x`
/usr/local/bin/nvi: symbolic link to ../Cellar/nvi/1.81.6_5/bin/nvi
接下来,稍微改变一下输出,这样我们就得到了一个“完整的”相对路径。 在这个例子中,我们只需要去掉中间的部分。
注意大写Y和小写x。可能有更简洁的方法。
$ Y=$(file -h `which $x` | sed "s/$x: symbolic link to //")
$ echo $Y
/usr/local/bin/../Cellar/nvi/1.81.6_5/bin/nvi
使用dirname,我们只得到路径部分。CD给它,名字就会自己清理干净。
$ cd `dirname $Y` ; pwd
/usr/local/Cellar/nvi/1.81.6_5/bin
这将我们引向UNIX的老技巧,即通过在括号/子shell中执行所有操作来“幽灵巡视”目录。这将有效地将我们返回到当前目录。
为了完整起见,我们可以将实际的文件名粘回末尾。
Ls甚至确保绝对路径是有效的,这是额外的加分。
$ ( cd `dirname ${Y}` ; ls `pwd`/${x} )
/usr/local/Cellar/nvi/1.81.6_5/bin/nvi
所以/usr/local/bin/nvi实际上是/usr/local/Cellar/nvi/1.81.6_5/bin/nvi
快速“转换”路径的简化示例:
$ (cd /usr/local/bin/../Cellar/nvi/1.81.6_5/bin ; pwd)
/usr/local/Cellar/nvi/1.81.6_5/bin
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/file.html https://pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html
更新之前的答案使用python 3
to_abs_path() {
python -c "import os; print (os.path.abspath('$1'))"
}
允许
to_abs_path "./../some_file.txt"