在bash中展开一个可能的相对path
作为我的脚本的参数有一些文件path。 那些当然可以是相对的(或者包含〜)。 但是对于我写的函数,我需要绝对的path,但没有解决它们的符号链接。
有这个function吗?
MY_PATH=$(readlink -f $YOUR_ARG)
将parsing相对path,如"./"
和"../"
考虑这个( 来源 ):
#!/bin/bash dir_resolve() { cd "$1" 2>/dev/null || return $? # cd to desired directory; if fail, quell any error messages but return exit status echo "`pwd -P`" # output full, link-resolved path } # sample usage if abs_path="`dir_resolve \"$1\"`" then echo "$1 resolves to $abs_path" echo pwd: `pwd` # function forks subshell, so working directory outside function is not affected else echo "Could not reach $1" fi
function abspath { if [[ -d "$1" ]] then pushd "$1" >/dev/null pwd popd >/dev/null elif [[ -e $1 ]] then pushd "$(dirname "$1")" >/dev/null echo "$(pwd)/$(basename "$1")" popd >/dev/null else echo "$1" does not exist! >&2 return 127 fi }
它使用pushd
/ popd
进入pwd
有用的状态。
简单的一行:
function abs_path { (cd "$(dirname '$1')" &>/dev/null && printf "%s/%s" "$PWD" "${1##*/}") }
用法:
function do_something { local file=$(abs_path $1) printf "Absolute path to %s: %s\n" "$1" "$file" } do_something $HOME/path/to/some\ where
我仍然试图弄清楚如何让它完全忘记path是否存在(所以它也可以在创build文件时使用)。
在OS X上,你可以使用
stat -f "%N" YOUR_PATH
在Linux上,你可能有realpath
可执行文件。 如果没有,以下可能会工作(不仅仅是链接):
readlink -c YOUR_PATH
这对于我在OS X上的技巧: $(cd SOME_DIRECTORY 2> /dev/null && pwd -P)
它应该在任何地方工作 其他解决scheme似乎太复杂了。
也许这更可读,不使用子shell,不改变当前目录:
dir_resolve() { local dir=`dirname "$1"` local file=`basename "$1"` pushd "$dir" &>/dev/null || return $? # On error, return error code echo "`pwd -P`/$file" # output full, link-resolved path with filename popd &> /dev/null }
自我编辑,我只注意到OP说他不是在寻找解决的符号链接:
“但是对于我写的函数,我需要的是绝对的path,但没有解决它们的符号链接。”
所以猜猜这毕竟不是那么适合他的问题。 🙂
自从我经历了这么多年以来,这次我需要一个纯粹的bash便携版本,我可以在OSX和linux上使用,我继续写下了一个:
生活版本住在这里:
https://github.com/keen99/shell-functions/tree/master/resolve_path
但为了这个目的,这里是目前的版本(我觉得它已经很好的testing了..但我很乐意回应!)
可能不难,使它适用于朴素贝壳(sh),但我没有尝试…我喜欢$ FUNCNAME太多。 🙂
#!/bin/bash resolve_path() { #I'm bash only, please! # usage: resolve_path <a file or directory> # follows symlinks and relative paths, returns a full real path # local owd="$PWD" #echo "$FUNCNAME for $1" >&2 local opath="$1" local npath="" local obase=$(basename "$opath") local odir=$(dirname "$opath") if [[ -L "$opath" ]] then #it's a link. #file or directory, we want to cd into it's dir cd $odir #then extract where the link points. npath=$(readlink "$obase") #have to -L BEFORE we -f, because -f includes -L :( if [[ -L $npath ]] then #the link points to another symlink, so go follow that. resolve_path "$npath" #and finish out early, we're done. return $? #done elif [[ -f $npath ]] #the link points to a file. then #get the dir for the new file nbase=$(basename $npath) npath=$(dirname $npath) cd "$npath" ndir=$(pwd -P) retval=0 #done elif [[ -d $npath ]] then #the link points to a directory. cd "$npath" ndir=$(pwd -P) retval=0 #done else echo "$FUNCNAME: ERROR: unknown condition inside link!!" >&2 echo "opath [[ $opath ]]" >&2 echo "npath [[ $npath ]]" >&2 return 1 fi else if ! [[ -e "$opath" ]] then echo "$FUNCNAME: $opath: No such file or directory" >&2 return 1 #and break early elif [[ -d "$opath" ]] then cd "$opath" ndir=$(pwd -P) retval=0 #done elif [[ -f "$opath" ]] then cd $odir ndir=$(pwd -P) nbase=$(basename "$opath") retval=0 #done else echo "$FUNCNAME: ERROR: unknown condition outside link!!" >&2 echo "opath [[ $opath ]]" >&2 return 1 fi fi #now assemble our output echo -n "$ndir" if [[ "x${nbase:=}" != "x" ]] then echo "/$nbase" else echo fi #now return to where we were cd "$owd" return $retval }
这是一个经典的例子,感谢酿造:
%% ls -l `which mvn` lrwxr-xr-x 1 draistrick 502 29 Dec 17 10:50 /usr/local/bin/mvn@ -> ../Cellar/maven/3.2.3/bin/mvn
使用这个函数,它会返回-real-path:
%% cat test.sh #!/bin/bash . resolve_path.inc echo echo "relative symlinked path:" which mvn echo echo "and the real path:" resolve_path `which mvn` %% test.sh relative symlinked path: /usr/local/bin/mvn and the real path: /usr/local/Cellar/maven/3.2.3/libexec/bin/mvn
你必须独占使用bash吗? 我需要这样做,厌倦了Linux和OS X之间的差异。所以我用PHP来做一个快速和肮脏的解决scheme。
#!/usr/bin/php <-- or wherever <?php { if($argc!=2) exit(); $fname=$argv[1]; if(!file_exists($fname)) exit(); echo realpath($fname)."\n"; } ?>
我知道这不是一个非常优雅的解决scheme,但它确实有效。