Linux 你如何在 Bash 中规范化文件路径?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/284662/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do you normalize a file path in Bash?
提问by Fabien
I want to transform /foo/bar/..
to /foo
我想转变/foo/bar/..
为/foo
Is there a bash command which does this?
是否有执行此操作的 bash 命令?
Edit: in my practical case, the directory does exist.
编辑:在我的实际情况下,该目录确实存在。
采纳答案by Kent Fredric
if you're wanting to chomp part of a filename from the path, "dirname" and "basename" are your friends, and "realpath" is handy too.
如果你想从路径中截取文件名的一部分,“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
realpath
alternatives
realpath
备择方案
If realpath
is not supported by your shell, you can try
如果realpath
您的外壳不支持,您可以尝试
readlink -f /path/here/..
Also
还
readlink -m /path/there/../../
Works the same as
工作原理与
realpath -s /path/here/../../
in that the path doesn't need to exist to be normalized.
因为路径不需要存在即可标准化。
回答by Adam Liss
Try realpath
. Below is the source in its entirety, hereby donated to the public domain.
试试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);
}
回答by Tim Whitcomb
I don't know if there is a direct bash command to do this, but I usually do
我不知道是否有直接的 bash 命令来执行此操作,但我通常会这样做
normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"
and it works well.
它运作良好。
回答by alhernau
Talkative, and a bit late answer. I need to write one since I'm stuck on older RHEL4/5. I handles absolute and relative links, and simplifies //, /./ and somedir/../ entries.
健谈,回答有点晚。我需要写一个,因为我被困在旧的 RHEL4/5 上。I 处理绝对和相对链接,并简化 //、/./ 和 somedir/../ 条目。
test -x /usr/bin/readlink || readlink () {
echo $(/bin/ls -l | /bin/cut -d'>' -f 2)
}
test -x /usr/bin/realpath || realpath () {
local PATH=/bin:/usr/bin
local inputpath=
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`
回答by mattalxndr
Use the readlink utility from the coreutils package.
使用 coreutils 包中的 readlink 实用程序。
MY_PATH=$(readlink -f "get_abs_path() {
local PARENT_DIR=$(dirname "")
cd "$PARENT_DIR"
local ABS_PATH="$(pwd)"/"$(basename "")"
cd - >/dev/null
echo "$ABS_PATH"
}
")
回答by loevborg
A portable and reliable solution is to use python, which is preinstalled pretty much everywhere (including Darwin). You have two options:
一个可移植且可靠的解决方案是使用 python,它几乎在任何地方(包括 Darwin)都预装。您有两个选择:
abspath
returns an absolute path but does not resolve symlinks:python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
realpath
returns an absolute path and in doing so resolves symlinks, generating a canonical path:python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
abspath
返回绝对路径但不解析符号链接:python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
realpath
返回一个绝对路径,并在这样做时解析符号链接,生成一个规范路径:python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
In each case, path/to/file
can be either a relative or absolute path.
在每种情况下,path/to/file
都可以是相对路径或绝对路径。
回答by Jeet
As Adam Liss noted realpath
is not bundled with every distribution. Which is a shame, because it is the best solution. The provided source code is great, and I will probably start using it now. Here is what I have been using until now, which I share here just for completeness:
正如 Adam Liss 所指出的realpath
,并不是每个发行版都捆绑在一起。这是一种耻辱,因为它是最好的解决方案。提供的源代码很棒,我现在可能会开始使用它。这是我到目前为止一直在使用的内容,为了完整起见,我在这里分享:
pushd foo/bar/..
dir=`pwd`
popd
If you want it to resolve symlinks, just replace pwd
with pwd -P
.
如果您希望它解析符号链接,只需替换pwd
为pwd -P
.
回答by schmunk
My recent solution was:
我最近的解决方案是:
for f in $paths; do (cd $f; pwd); done
Based on the answer of Tim Whitcomb.
基于 Tim Whitcomb 的回答。
回答by Jesse Glick
Not exactly an answer but perhaps a follow-up question (original question was not explicit):
不完全是答案,但可能是后续问题(原始问题不明确):
readlink
is fine if you actually want to follow symlinks. But there is also a use case for merely normalizing ./
and ../
and //
sequences, which can be done purely syntactically, withoutcanonicalizing symlinks. readlink
is no good for this, and neither is realpath
.
readlink
如果您确实想遵循符号链接,那很好。但也有一个用例仅规范化./
and../
和//
序列,这可以纯粹在语法上完成,而无需规范化符号链接。readlink
对这没有好处,也不是realpath
。
jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths
works for existing paths, but breaks for others.
适用于现有路径,但适用于其他路径。
A sed
script would seem to be a good bet, except that you cannot iteratively replace sequences (/foo/bar/baz/../..
-> /foo/bar/..
-> /foo
) without using something like Perl, which is not safe to assume on all systems, or using some ugly loop to compare the output of sed
to its input.
一个sed
脚本似乎是一个不错的选择,但你不能反复更换序列(/foo/bar/baz/../..
- > /foo/bar/..
- > /foo
),而不使用像Perl中,这是不是安全地假定所有系统上,或者使用一些丑陋的环路的输出比较sed
来它的输入。
FWIW, a one-liner using Java (JDK 6+):
FWIW,使用 Java (JDK 6+) 的单线:
resolve_dir() {
(builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi)
}
回答by apottere
I'm late to the party, but this is the solution I've crafted after reading a bunch of threads like this:
我迟到了,但这是我在阅读了一堆这样的线程后精心制作的解决方案:
##代码##This will resolve the absolute path of $1, play nice with ~, keep symlinks in the path where they are, and it won't mess with your directory stack. It returns the full path or nothing if it doesn't exist. It expects $1 to be a directory and will probably fail if it's not, but that's an easy check to do yourself.
这将解析 $1 的绝对路径,与 ~ 一起玩,将符号链接保留在它们所在的路径中,并且不会弄乱您的目录堆栈。如果不存在,则返回完整路径或不返回任何内容。它期望 $1 是一个目录,如果不是,则可能会失败,但您自己可以轻松检查。