我想将/foo/bar/..
转换为/foo
是否有bash命令执行此操作?
编辑:在我的实际案例中,该目录确实存在。
答案 0 :(得分:167)
如果您想要从路径中选择部分文件名,“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
替代
如果您的shell不支持realpath
,则可以尝试
readlink -f /path/here/..
另外
readlink -m /path/there/../../
与
相同realpath -s /path/here/../../
因为路径不需要存在才能被标准化。
答案 1 :(得分:90)
我不知道是否有直接的bash命令来执行此操作,但我通常会这样做
normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"
并且效果很好。
答案 2 :(得分:53)
试试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);
}
答案 3 :(得分:35)
便携可靠的解决方案是使用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
可以是相对路径或绝对路径。
答案 4 :(得分:34)
使用coreutils包中的readlink实用程序。
MY_PATH=$(readlink -f "$0")
答案 5 :(得分:13)
readlink
是获取绝对路径的bash标准。如果路径或路径不存在,它还具有返回空字符串的优点(给定标志可以这样做)。
要获取可能存在或可能不存在的目录的绝对路径,但是父母确实存在,请使用:
abspath=$(readlink -f $path)
获取必须与所有父项一起存在的目录的绝对路径:
abspath=$(readlink -e $path)
规范化给定路径并遵循符号链接(如果它们碰巧存在),否则忽略丢失的目录并且只返回路径,它是:
abspath=$(readlink -m $path)
唯一的缺点是readlink将遵循链接。如果您不想关注链接,可以使用此替代约定:
abspath=$(cd ${path%/*} && echo $PWD/${path##*/})
这将是chd到$ path的目录部分并打印当前目录以及$ path的文件部分。如果它没有chdir,你得到一个空字符串和stderr上的错误。
答案 6 :(得分:9)
老问题,但是如果你在shell级别处理完整路径名,那么有更简单的方法:
abspath="$( cd "$path" && pwd )"
由于cd在子shell中发生,因此不会影响主脚本。
假设您的shell内置命令接受-L和-P,有两种变体:
abspath="$( cd -P "$path" && pwd -P )" #physical path with resolved symlinks abspath="$( cd -L "$path" && pwd -L )" #logical path preserving symlinks
就个人而言,除非出于某种原因对符号链接着迷,否则我很少需要这种方法。
仅供参考:获取脚本起始目录的变化即使脚本稍后更改其当前目录也会起作用。
name0="$(basename "$0")"; #base name of script dir0="$( cd "$( dirname "$0" )" && pwd )"; #absolute starting dir
使用CD可以确保您始终拥有绝对目录,即使脚本是由./script.sh等命令运行的,如果没有cd / pwd,通常只会给出...如果脚本执行cd则无用稍后的。
答案 7 :(得分:7)
我最近的解决方案是:
pushd foo/bar/..
dir=`pwd`
popd
根据Tim Whitcomb的回答。
答案 8 :(得分:7)
正如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
。
答案 9 :(得分:5)
不完全是一个答案,但也许是一个后续问题(原始问题不明确):
如果您真的想要遵循符号链接,那么 readlink
就可以了。但是,还有一个用例仅用于规范./
和../
和//
序列,这可以完全在语法上完成,没有规范化符号链接。 readlink
对此无益,realpath
也不是。
for f in $paths; do (cd $f; pwd); done
适用于现有路径,但适用于其他路径。
sed
脚本似乎是一个不错的选择,除了你不能迭代地替换序列(/foo/bar/baz/../..
- &gt; /foo/bar/..
- &gt; /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
答案 10 :(得分:4)
我迟到了,但这是我在阅读了这样一堆线程后制作的解决方案:
resolve_dir() {
(builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi)
}
这将解析$ 1的绝对路径,与〜一起使用,将符号链接保留在它们所在的路径中,并且它不会弄乱你的目录堆栈。它返回完整路径,如果不存在则返回任何内容。它希望1美元成为一个目录,如果不是,它可能会失败,但这是一个很容易自己做的检查。
答案 11 :(得分:4)
健谈,有点迟到的回答。我需要写一个,因为我被困在较旧的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`
答案 12 :(得分:3)
尝试使用我们在GitHub上免费且无阻碍使用的新Bash库产品realpath-lib。它是完整的文档,是一个很好的学习工具。
它解析了本地路径,相对路径和绝对路径,除了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的函数。跨平台尝试,并帮助改进它。
答案 13 :(得分:2)
基于@Andre的答案,我可能会有一个稍好的版本,以防有人在完全基于字符串操作的循环解决方案之后。对于那些不想取消引用任何符号链接的人来说,它也很有用,这是使用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
答案 14 :(得分:1)
realpath
的问题在于它在BSD(或OSX)上不可用。这是一个从a rather old (2009) article from Linux Journal中提取的简单配方,非常便携:
function normpath() {
# Remove all /./ sequences.
local path=${1//\/.\//\/}
# Remove dir/.. sequences.
while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
path=${path/${BASH_REMATCH[0]}/}
done
echo $path
}
请注意,此变体也不要求存在路径。
答案 15 :(得分:0)
基于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
答案 16 :(得分:0)
FILEPATH="file.txt"
echo $(realpath $(dirname $FILEPATH))/$(basename $FILEPATH)
即使文件不存在,这也有效。它确实需要包含该文件的目录。
答案 17 :(得分:0)
我知道这是一个古老的问题。我还在提供替代方案。最近我遇到了同样的问题,发现没有现成的便携式命令来做到这一点。所以我编写了以下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
}
答案 18 :(得分:0)
我需要一个可以完成所有这三项的解决方案:
realpath
和readlink -f
是插件没有一个答案同时包含#1和#2。我添加了#3以保存其他任何进一步的牦牛皮。
#!/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 "
答案 19 :(得分:0)
我做了一个内置的函数来处理这个问题,重点放在尽可能高的性能上(以娱乐为目的)。它不解析符号链接,因此与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倍,主要是因为不需要每次都执行新程序。
答案 20 :(得分:0)
如果你只想规范化一个路径,无论存在或不存在,不接触文件系统,不解析任何链接,不使用外部工具,这里是一个从 Python 的 { 翻译过来的纯 Bash 函数{1}}。
posixpath.normpath
示例:
#!/usr/bin/env bash
# Normalize path, eliminating double slashes, etc.
# Usage: new_path="$(normpath "${old_path}")"
# Translated from Python's posixpath.normpath:
# https://github.com/python/cpython/blob/master/Lib/posixpath.py#L337
normpath() {
local IFS=/ initial_slashes='' comp comps=()
if [[ $1 == /* ]]; then
initial_slashes='/'
[[ $1 == //* && $1 != ///* ]] && initial_slashes='//'
fi
for comp in $1; do
[[ -z ${comp} || ${comp} == '.' ]] && continue
if [[ ${comp} != '..' || (-z ${initial_slashes} && ${#comps[@]} -eq 0) || (\
${#comps[@]} -gt 0 && ${comps[-1]} == '..') ]]; then
comps+=("${comp}")
elif ((${#comps[@]})); then
unset 'comps[-1]'
fi
done
comp="${initial_slashes}${comps[*]}"
printf '%s\n' "${comp:-.}"
}
就我个人而言,我无法理解为什么 Shell(一种经常用于操作文件的语言)不提供处理路径的基本功能。在 python 中,我们有很好的库,比如 os.path 或 pathlib,它提供了一大堆工具来提取文件名、扩展名、基名、路径段、分割或连接路径,获得绝对或规范化路径,确定路径之间的关系,没有太多的大脑做任何事情。他们处理边缘情况,而且很可靠。在 Shell 中,要执行其中任何一项操作,我们要么调用外部可执行文件,要么必须使用这些极其简陋和晦涩的语法重新发明轮子...
答案 21 :(得分:-1)
我今天发现您可以使用stat
命令来解析路径。
对于像&#34;〜/ Documents&#34;:
这样的目录你可以运行:
stat -f %N ~/Documents
获取完整路径:
/Users/me/Documents
对于符号链接,您可以使用%Y格式选项:
stat -f %Y example_symlink
可能会返回如下结果:
/usr/local/sbin/example_symlink
* NIX的其他版本的格式化选项可能有所不同,但这些在OSX上适用于我。
答案 22 :(得分:-3)
使用node.js
的简单解决方案:
#!/usr/bin/env node
process.stdout.write(require('path').resolve(process.argv[2]));