你如何正常化Bash中的文件path?
我想将/foo/bar/..
为/foo
有没有这样做的bash命令?
编辑:在我的实际情况下,目录确实存在。
如果你想从path中剔除一部分文件名,“dirname”和“basename”是你的朋友,“realpath”也是很方便的。
dirname /foo/bar/baz # /foo/bar basename /foo/bar/baz # baz dirname $( dirname /foo/bar/baz )) # /foo realpath ../foo # ../foo: No such file or directory realpath /tmp/../tmp/../tmp # /tmp
编辑
实际path似乎不是标准问题。
最接近你可以得到的股票标准是
readlink -f /path/here/..
Realpath似乎来自debian,不是coreutils的一部分: http ://packages.debian.org/unstable/utils/realpath原来是DWWW包的一部分。
(也可以在应用程序pipe理/实时pathgentoo)
readlink -m /path/there/../../
和。一样工作
realpath -s /path/here/../../
因为它不需要实际存在的path来规范化它。
我不知道是否有一个直接的bash命令来做到这一点,但我通常这样做
normalDir="`cd "${dirToNormalize}";pwd`" echo "${normalDir}"
它运作良好。
尝试realpath
。 以下是全部内容,特此捐赠给公共领域。
// realpath.c: display the absolute path to a file or directory. // Adam Liss, August, 2007 // This program is provided "as-is" to the public domain, without express or // implied warranty, for any non-profit use, provided this notice is maintained. #include <stdio.h> #include <stdlib.h> #include <string.h> #include <libgen.h> #include <limits.h> static char *s_pMyName; void usage(void); int main(int argc, char *argv[]) { char sPath[PATH_MAX]; s_pMyName = strdup(basename(argv[0])); if (argc < 2) usage(); printf("%s\n", realpath(argv[1], sPath)); return 0; } void usage(void) { fprintf(stderr, "usage: %s PATH\n", s_pMyName); exit(1); }
使用coreutils软件包中的readlink实用程序。
MY_PATH=$(readlink -f "$0")
一个便携和可靠的解决scheme是使用python,它预装在几乎所有地方(包括达尔文)。 你有两个select:
-
abspath
返回绝对path,但不能parsing符号链接:python -c "import os,sys; print os.path.abspath(sys.argv[1])" path/to/file
-
realpath
返回一个绝对path,并在这样做parsing符号链接,生成一个规范的path:python -c "import os,sys; print os.path.realpath(sys.argv[1])" path/to/file
在每种情况下, path/to/file
可以是相对path或绝对path。
readlink
是获取绝对path的bash标准。 它还具有返回空string的优点,如果path或path不存在(给定的标志这样做)。
要获得可能存在或不存在的目录的绝对path,但是父母确实存在,请使用:
abspath=$(readlink -f $path)
为了获得必须与所有父母一起存在的目录的绝对path:
abspath=$(readlink -e $path)
要规范化给定的path,并遵循符号链接,如果他们碰巧存在,但否则忽略丢失的目录,只是返回path,它是:
abspath=$(readlink -m $path)
唯一的缺点是readlink会跟随链接。 如果你不想跟随链接,你可以使用这个替代的约定:
abspath=$(cd ${path%/*} && echo $PWD/${path##*/})
这将chdir到$ path的目录部分,并打印当前目录以及$ path的文件部分。 如果chdir失败,你会得到一个空string,并在stderr上出错。
我最近的解决scheme是:
pushd foo/bar/.. dir=`pwd` popd
基于Tim Whitcomb的回答。
正如Adam Liss指出的, realpath
并不是与每个发行版捆绑在一起的。 这是一个耻辱,因为这是最好的解决scheme。 提供的源代码非常好,现在我可能会开始使用它。 以下是我迄今为止所使用的内容,我只是为了完整而分享这些内容:
get_abs_path() { local PARENT_DIR=$(dirname "$1") cd "$PARENT_DIR" local ABS_PATH="$(pwd)"/"$(basename "$1")" cd - >/dev/null echo "$ABS_PATH" }
如果你想要parsing符号链接,只需用pwd -P
replacepwd
。
健谈,还有一点迟到的答案。 我需要写一个,因为我被困在较旧的RHEL4 / 5上。 我处理绝对和相对链接,并简化//,/./和somedir /../条目。
test -x /usr/bin/readlink || readlink () { echo $(/bin/ls -l $1 | /bin/cut -d'>' -f 2) } test -x /usr/bin/realpath || realpath () { local PATH=/bin:/usr/bin local inputpath=$1 local changemade=1 while [ $changemade -ne 0 ] do changemade=0 local realpath="" local token= for token in ${inputpath//\// } do case $token in ""|".") # noop ;; "..") # up one directory changemade=1 realpath=$(dirname $realpath) ;; *) if [ -h $realpath/$token ] then changemade=1 target=`readlink $realpath/$token` if [ "${target:0:1}" = '/' ] then realpath=$target else realpath="$realpath/$target" fi else realpath="$realpath/$token" fi ;; esac done inputpath=$realpath done echo $realpath } mkdir -p /tmp/bar (cd /tmp ; ln -s /tmp/bar foo; ln -s ../.././usr /tmp/bar/link2usr) echo `realpath /tmp/foo`
不完全是一个答案,但也许是一个后续问题(原始问题并不明确):
如果你真的想遵循符号链接, readlink
是好的。 但是也有一个用例,它只是对./
和../
和//
序列进行规范化,可以在语法上纯粹地完成, 而不需要规范化符号链接。 readlink
对此并不好,也不是readlink
。
for f in $paths; do (cd $f; pwd); done
为现有的path工作,但为别人打破。
一个sed
脚本似乎是一个很好的select,除了你不能使用像Perl这样的东西来迭代地replace序列( /foo/bar/..
/foo/bar/baz/../..
> /foo/bar/..
/foo/bar/baz/../..
> /foo
) ,这在所有系统上都是不安全的,或者使用一些丑陋的循环来比较sed
的输出和它的input。
FWIW,一个使用Java(JDK 6+)的单线程:
jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths
我迟到了,但是这是我读过一堆这样的线程后制作的解决scheme:
resolve_dir() { (builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi) }
这将解决$ 1的绝对path,和〜一起玩,在它们所在的path上保留符号链接,并且不会混淆你的目录栈。 它返回完整的path,或者如果不存在,则返回任何内容。 它预计1美元是一个目录,如果不是,可能会失败,但这是一个简单的检查自己做。
旧的问题,但是如果你在shell级别处理完整的path名 ,则有更简单的方法:
abspath =“$(cd”$ path“&& pwd)”
由于cd发生在一个子shell中,它不会影响主脚本。
假设您的shell内置命令接受-L和-P,两个变体是:
abspath =“$(cd -P”$ path“&& pwd -P)”#已parsing符号链接的#物理path abspath =“$(cd -L”$ path“&& pwd -L)”#逻辑path保留符号链接
就个人而言,我很less需要这种方法,除非我因某种原因而着迷于符号链接。
仅供参考:即使脚本稍后改变其当前目录,也可以获得脚本的起始目录。
name0 =“$(basename”$ 0“)”; #脚本的基本名称 dir0 =“$(cd”$(dirname“$ 0”)“&& pwd)”; #absolute开始目录
CD的使用保证你总是拥有绝对的目录,即使脚本是通过诸如./script.sh之类的命令来运行的,而没有cd / pwd的话,它经常会给出这个目录。
试试我们已经放在GitHub上的新的Bash库产品realpath-lib ,免费和无阻碍的使用。 它被彻底logging,并成为一个伟大的学习工具。
它解决了局部的,相对的和绝对的path,除了Bash 4+之外没有任何依赖关系; 所以它应该在任何地方工作。 它是免费的,干净的,简单而有益的。
你可以做:
get_realpath <absolute|relative|symlink|local file path>
这个函数是库的核心:
function get_realpath() { if [[ -f "$1" ]] then # file *must* exist if cd "$(echo "${1%/*}")" &>/dev/null then # file *may* not be local # exception is ./file.ext # try 'cd .; cd -;' *works!* local tmppwd="$PWD" cd - &>/dev/null else # file *must* be local local tmppwd="$PWD" fi else # file *cannot* exist return 1 # failure fi # reassemble realpath echo "$tmppwd"/"${1##*/}" return 0 # success }
它还包含函数get_dirname,get_filename,get_ stemname和validate_path。 尝试跨平台,并帮助改善它。
实际realpath
的问题是它不适用于BSD(或OSX)。 这是一个简单的配方, 从Linux Journal的一篇相当古老的文章(2009)中提取出来,非常便携:
function normpath() { # Remove all /./ sequences. local path=${1//\/.\//\/} # Remove dir/.. sequences. while [[ $path =~ ([^/][^/]*/\.\./) ]]; do path=${path/${BASH_REMATCH[0]}/} done echo $path }
注意这个变体也不需要存在的path。
基于@安德烈的回答,我可能会有一个稍微好一点的版本,以防有人在无循环,完全string操作的解决scheme之后。 对于那些不想解引用任何符号链接的人来说也是有用的,这是使用realpath
或者readlink -f
的缺点。
它适用于bash版本3.2.25及更高版本。
shopt -s extglob normalise_path() { local path="$1" # get rid of /../ example: /one/../two to /two path="${path//\/*([!\/])\/\.\./}" # get rid of /./ and //* example: /one/.///two to /one/two path="${path//@(\/\.\/|\/+(\/))//}" # remove the last '/.' echo "${path%%/.}" } $ normalise_path /home/codemedic/../codemedic////.config /home/codemedic/.config
今天我发现你可以使用stat
命令来parsingpath。
所以对于像“〜/ Documents”这样的目录:
你可以运行这个:
stat -f %N ~/Documents
要获得完整path:
/Users/me/Documents
对于符号链接,可以使用%Y格式选项:
stat -f %Y example_symlink
其中可能会返回如下结果:
/usr/local/sbin/example_symlink
在其他版本的* NIX格式化选项可能会有所不同,但这些在OSX上为我工作。
基于loveborg的优秀python片段,我写这个:
#!/bin/sh # Version of readlink that follows links to the end; good for Mac OS X for file in "$@"; do while [ -h "$file" ]; do l=`readlink $file` case "$l" in /*) file="$l";; *) file=`dirname "$file"`/"$l" esac done #echo $file python -c "import os,sys; print os.path.abspath(sys.argv[1])" "$file" done
一个简单的解决scheme,使用node.js
:
#!/usr/bin/env node process.stdout.write(require('path').resolve(process.argv[2]));