我想转变/foo/bar/..
成/foo
是否有执行此操作的bash命令?
编辑:在我的实际情况下,该目录确实存在。
/foo/bar/..
为/foo
,并使用bash
命令。如果还有其他未说明的要求,那么也许应该...
我想转变/foo/bar/..
成/foo
是否有执行此操作的bash命令?
编辑:在我的实际情况下,该目录确实存在。
/foo/bar/..
为/foo
,并使用bash
命令。如果还有其他未说明的要求,那么也许应该...
Answers:
如果您想从路径中截取一部分文件名,则“ 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
备择方案
如果realpath
您的外壳不支持,则可以尝试
readlink -f /path/here/..
也
readlink -m /path/there/../../
与...相同
realpath -s /path/here/../../
因为不需要标准化路径。
realpath
和readlink
都来自GNU核心实用程序,因此很可能两者都不存在。如果我没记错的话,Mac上的readlink版本与GNU版本稍有不同:-\
我不知道是否有直接的bash命令来执行此操作,但我通常会这样做
normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"
而且效果很好。
rm -rf $normalDir
如果dirToNormalize不存在,这可能非常危险()!
&&
@DavidBlevins的注释。
尝试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);
}
realpath
。它也恰好是我的最爱,而不是从源头上编译您的工具。全部都是重量级的,而且大多数时候您只想要readlink -f
... BTW,readlink
它也不是内置的bash,而是coreutils
Ubuntu上的一部分。
一个可移植且可靠的解决方案是使用python,它几乎在所有地方(包括Darwin)都已预先安装。您有两种选择:
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
在每种情况下,path/to/file
可以是相对路径或绝对路径。
使用coreutils软件包中的readlink实用程序。
MY_PATH=$(readlink -f "$0")
readlink
是获取绝对路径的bash标准。如果路径或路径不存在,则它还具有返回空字符串的优点(考虑到这样做的标志)。
要获取可能存在或不存在但父母确实存在的目录的绝对路径,请使用:
abspath=$(readlink -f $path)
要获取必须与所有父目录一起存在的目录的绝对路径,请执行以下操作:
abspath=$(readlink -e $path)
为了规范化给定的路径并遵循符号链接(如果它们确实存在),否则忽略缺少的目录,无论如何只是返回路径,它是:
abspath=$(readlink -m $path)
唯一的缺点是readlink将跟随链接。如果您不想跟随链接,则可以使用以下替代约定:
abspath=$(cd ${path%/*} && echo $PWD/${path##*/})
这将chdir到$ path的目录部分,并打印当前目录以及$ path的文件部分。如果无法执行chdir,则会收到一个空字符串,并在stderr上显示错误。
readlink
如果可用,是一个不错的选择。OS X版本不支持-e
或-f
选项。在前三个示例中,应该使用双引号引起来$path
以处理文件名中的空格或通配符。+1用于参数扩展,但这具有安全漏洞。如果path
为空,它将cd
进入您的主目录。您需要双引号。abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
这是个老问题,但是如果要在Shell级别处理完整路径名,则有一种更简单的方法:
abspath =“ $(cd” $ path“ && pwd)”
由于CD发生在子外壳中,因此不会影响主脚本。
假设您的shell内置命令接受-L和-P,有两种变体:
abspath =“ $(cd -P” $ path“ && pwd -P)”#具有已解析符号链接的物理路径 abspath =“ $(cd -L” $ path“ && pwd -L)”#保留逻辑路径的符号链接
就我个人而言,除非出于某种原因我对符号链接着迷,否则我很少需要这种稍后的方法。
仅供参考:获取脚本开始目录的变体,即使该脚本稍后更改了它的当前目录,该脚本仍然有效。
name0 =“ $(基本名称” $ 0“)”; #脚本的基本名称 dir0 =“ $(cd” $(目录名“ $ 0”)“ && pwd)”; #绝对起始目录
使用CD可以确保您始终拥有绝对目录,即使脚本由./script.sh之类的命令运行,如果没有cd / pwd,该命令通常也只会提供..如果脚本稍后再执行cd,则无用。
正如亚当·利斯(Adam Liss)所指出的realpath
,并不是每个发行版都捆绑了它。真可惜,因为这是最好的解决方案。提供的源代码很棒,我现在可能会开始使用它。这是到目前为止我一直在使用的内容,为了完整起见,在此分享:
get_abs_path() {
local PARENT_DIR=$(dirname "$1")
cd "$PARENT_DIR"
local ABS_PATH="$(pwd)"/"$(basename "$1")"
cd - >/dev/null
echo "$ABS_PATH"
}
如果您希望它解析符号链接,只需替换pwd
为pwd -P
。
pwd -P
这种情况下选项的陷阱……考虑一下,如果$(basename "$1")
符号链接到另一个目录中的文件,将会发生什么。该pwd -P
只解决符号链接路径的目录部分,但不是基本名称部分。
不完全是一个答案,而是一个后续问题(原始问题并不明确):
readlink
如果您实际上想遵循符号链接,那很好。但是,还有一个仅用于规范化./
和../
和//
序列的用例,可以纯粹通过语法来完成,而无需规范化符号链接。readlink
这样做是没有好处的,也不是realpath
。
for f in $paths; do (cd $f; pwd); done
适用于现有路径,但适用于其他路径。
一个sed
脚本似乎是一个不错的选择,但你不能反复更换序列(/foo/bar/baz/../..
- > /foo/bar/..
- > /foo
),而不使用像Perl中,这是不是安全地假定所有系统上,或者使用一些丑陋的环路的输出比较sed
来它的输入。
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
realpath
可以-s
选择不解析符号链接,而仅解析对的引用/./
,/../
并删除多余的/
字符。当与该-m
选项组合使用时,realpath
仅对文件名起作用,而不接触任何实际文件。这听起来像是一个完美的解决方案。但是可惜的realpath
是,在许多系统上仍然缺少。
..
当涉及符号链接时,无法从句法上删除组件。 /one/two/../three
是不一样的/one/three
,如果two
是一个符号链接/foo/bar
。
健谈,回答迟了一点。由于我停留在较旧的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`
尝试我们免费提供给我们的新的Bash库产品realpath-lib,该产品已在GitHub上免费提供且不受限制。它已被详细记录,并且是一个很好的学习工具。
它解析本地,相对和绝对路径,除了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_temname和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
}
注意,此变体也不需要路径。
根据@Andre的回答,如果有人使用了无循环的,完全基于字符串操作的解决方案,我可能会提供一个更好的版本。对于不想取消引用任何符号链接的人也很有用,这是使用realpath
or 的缺点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
hello/../world
(2)文件名中的点/hello/..world
(3)双斜杠后的点/hello//../world
(4)双斜杠之前或之后的点 /hello//./world
或 /hello/.//world
(5)当前后的父项:/hello/./../world/
(6)父项父后: /hello/../../world
等- 一些,这些可以通过使用一个循环,直到路径停止变化改正固定。(也删除dir/../
,而不是 从末尾/dir/..
删除dir/..
。)
基于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
FILEPATH="file.txt"
echo $(realpath $(dirname $FILEPATH))/$(basename $FILEPATH)
即使文件不存在,此方法也有效。它确实需要包含该文件的目录。
realpath -e
我需要一个可以同时完成这三个任务的解决方案:
realpath
并且readlink -f
是插件答案都没有#1和#2。我添加了#3来保存其他further牛草。
#!/bin/bash
P="${1?Specify a file path}"
[ -e "$P" ] || { echo "File does not exist: $P"; exit 1; }
while [ -h "$P" ] ; do
ls="$(ls -ld "$P")"
link="$(expr "$ls" : '.*-> \(.*\)$')"
expr "$link" : '/.*' > /dev/null &&
P="$link" ||
P="$(dirname "$P")/$link"
done
echo "$(cd "$(dirname "$P")"; pwd)/$(basename "$P")"
这是一个简短的测试用例,在路径中有一些扭曲的空格,可以完全行使报价
mkdir -p "/tmp/test/ first path "
mkdir -p "/tmp/test/ second path "
echo "hello" > "/tmp/test/ first path / red .txt "
ln -s "/tmp/test/ first path / red .txt " "/tmp/test/ second path / green .txt "
cd "/tmp/test/ second path "
fullpath " green .txt "
cat " green .txt "
我知道这是一个古老的问题。我仍在提供替代方案。最近,我遇到了同样的问题,发现没有现成的可移植命令可以做到这一点。因此,我编写了以下shell脚本,其中包括一个可以完成上述任务的函数。
#! /bin/sh
function normalize {
local rc=0
local ret
if [ $# -gt 0 ] ; then
# invalid
if [ "x`echo $1 | grep -E '^/\.\.'`" != "x" ] ; then
echo $1
return -1
fi
# convert to absolute path
if [ "x`echo $1 | grep -E '^\/'`" == "x" ] ; then
normalize "`pwd`/$1"
return $?
fi
ret=`echo $1 | sed 's;/\.\($\|/\);/;g' | sed 's;/[^/]*[^/.]\+[^/]*/\.\.\($\|/\);/;g'`
else
read line
normalize "$line"
return $?
fi
if [ "x`echo $ret | grep -E '/\.\.?(/|$)'`" != "x" ] ; then
ret=`normalize "$ret"`
rc=$?
fi
echo "$ret"
return $rc
}
https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c
我做了一个仅内置的函数来处理此问题,重点放在尽可能高的性能上(以娱乐为目的)。它不会解析符号链接,因此与基本上相同realpath -sm
。
## A bash-only mimic of `realpath -sm`.
## Give it path[s] as argument[s] and it will convert them to clean absolute paths
abspath () {
${*+false} && { >&2 echo $FUNCNAME: missing operand; return 1; };
local c s p IFS='/'; ## path chunk, absolute path, input path, IFS for splitting paths into chunks
local -i r=0; ## return value
for p in "$@"; do
case "$p" in ## Check for leading backslashes, identify relative/absolute path
'') ((r|=1)); continue;;
//[!/]*) >&2 echo "paths =~ ^//[^/]* are impl-defined; not my problem"; ((r|=2)); continue;;
/*) ;;
*) p="$PWD/$p";; ## Prepend the current directory to form an absolute path
esac
s='';
for c in $p; do ## Let IFS split the path at '/'s
case $c in ### NOTE: IFS is '/'; so no quotes needed here
''|.) ;; ## Skip duplicate '/'s and '/./'s
..) s="${s%/*}";; ## Trim the previous addition to the absolute path string
*) s+=/$c;; ### NOTE: No quotes here intentionally. They make no difference, it seems
esac;
done;
echo "${s:-/}"; ## If xpg_echo is set, use `echo -E` or `printf $'%s\n'` instead
done
return $r;
}
注意:此函数不能处理以开头的路径//
,因为在路径开头恰好有两个双斜杠是实现定义的行为。但是,它可以处理/
,,///
依此类推。
这个函数似乎可以正确处理所有边缘情况,但是可能还有一些我没有处理过的情况。
性能说明:当调用数千个参数时,其abspath
运行速度比realpath -sm
; 慢约10倍;当使用单个参数调用时,其abspath
运行速度比realpath -sm
我的计算机快110倍以上,这主要是因为不需要每次都执行新程序。
我今天发现您可以使用该stat
命令来解析路径。
因此,对于“〜/ Documents”之类的目录:
您可以运行以下命令:
stat -f %N ~/Documents
要获取完整路径:
/Users/me/Documents
对于符号链接,可以使用%Y格式选项:
stat -f %Y example_symlink
可能返回如下结果:
/usr/local/sbin/example_symlink
格式选项在* NIX的其他版本上可能有所不同,但这些选项在OSX上对我有用。
stat -f %N ~/Documents
行是一条红色鲱鱼...您的shell替换~/Documents
为/Users/me/Documents
,并stat
逐字打印其参数。
/foo/bar
甚至/foo
实际上是否存在,还是仅根据路径名规则对字符串操作方面感兴趣?