Bash에서 파일 경로를 어떻게 정규화합니까?
나는 변신하고 싶습니다./foo/bar/..
/foo
이를 수행하는 bash 명령어가 있습니까?
편집: 실제로는 디렉터리가 존재합니다.
경로에서 파일 이름의 일부를 잘라내려면 "이름"과 "기본 이름"이 친구이며 "실제 경로"도 편리합니다.
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/../../
경로가 존재할 필요가 없다는 점에서 정규화됩니다.
이를 위한 직접적인 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);
}
휴대하기 쉽고 안정적인 솔루션은 거의 모든 곳(다윈 포함)에 사전 설치된 파이썬을 사용하는 것입니다.두 가지 옵션이 있습니다.
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")
오래된 질문이지만 셸 수준에서 전체 경로 이름을 처리하는 경우 훨씬 더 간단한 방법이 있습니다.
abspath="$( cd "$path" & & pwd )"
CD는 하위 셸에서 발생하므로 기본 스크립트에 영향을 주지 않습니다.
셸 기본 제공 명령이 -L 및 -P를 허용한다고 가정할 때 다음과 같은 두 가지 변형이 있습니다.
abspath="$( cd -P "$path" &&pwd -P )" # 확인된 심볼 링크가 있는 물리적 경로abspath="$( cd -L "$path" &&pwd -L )" #논리 경로 심볼릭 링크 유지
개인적으로, 제가 어떤 이유에서인지 상징적인 연결에 매료되지 않는 한, 저는 이 이후의 접근법이 거의 필요하지 않습니다.
참고로 스크립트가 나중에 현재 디렉터리를 변경하더라도 작동하는 스크립트의 시작 디렉터리를 얻는 것에 대한 변동입니다.
name0="$(기본 이름 "$0")"; #스크립트의 기본 이름dir0="$( cd "$(dirname "$0" ) &&pwd )", #절대 시작 dir
CD를 사용하면 스크립트가 ./script와 같은 명령으로 실행되는 경우에도 항상 절대 디렉터리를 사용할 수 있습니다.cd/pwd 없이 종종 그냥 주는 sh.스크립트가 나중에 cd를 수행하면 사용할 수 없습니다.
readlink
절대 경로를 얻기 위한 bash 표준입니다.또한 경로 또는 경로가 존재하지 않는 경우 빈 문자열을 반환할 수 있는 이점이 있습니다(이를 위해 플래그가 지정됨).
존재하거나 존재하지 않지만 존재하는 부모가 있는 디렉토리에 대한 절대 경로를 가져오려면 다음을 사용합니다.
abspath=$(readlink -f $path)
모든 부모와 함께 존재해야 하는 디렉토리에 대한 절대 경로를 가져오려면:
abspath=$(readlink -e $path)
지정된 경로를 표준화하고 심볼릭 링크가 존재하는 경우 심볼릭 링크를 따르지만 그렇지 않으면 누락된 디렉토리를 무시하고 경로만 반환하는 것은 다음과 같습니다.
abspath=$(readlink -m $path)
유일한 단점은 읽기 링크가 링크를 따라간다는 것입니다.링크를 따르지 않으려면 다음과 같은 대체 규칙을 사용할 수 있습니다.
abspath=$(cd ${path%/*} && echo $PWD/${path##*/})
그러면 $path의 디렉터리 부분으로 chdir가 이동하고 $path의 파일 부분과 함께 현재 디렉터리가 인쇄됩니다.chdir에 실패하면 stderr에서 빈 문자열과 오류가 발생합니다.
아담 리스가 언급한 바야흐로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
.
최근의 해결책은 다음과 같습니다.
pushd foo/bar/..
dir=`pwd`
popd
팀 휘트컴의 답변을 토대로 합니다.
정확한 답변은 아니지만 후속 질문일 수 있습니다(원래 질문은 명시적이지 않았습니다.
readlink
심볼릭 링크를 실제로 따르고 싶다면 괜찮습니다.하지만 단순히 정상화하는 것에 대한 사용 사례도 있습니다../
그리고.../
그리고.//
심볼릭 링크를 표준화하지 않고 순수하게 구문적으로 수행할 수 있는 시퀀스. readlink
이것에 좋지 않고, 또한 좋지 않습니다.realpath
.
for f in $paths; do (cd $f; pwd); done
기존 경로에서는 작동하지만 다른 경로에서는 중단됩니다.
A 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
파티에 늦었지만 다음과 같은 내용을 읽고 해결책을 마련했습니다.
resolve_dir() {
(builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi)
}
이렇게 하면 $1의 절대 경로가 해결되고 ~와 함께 잘 작동하며 심볼릭 링크를 있는 경로에 유지하면 디렉터리 스택이 손상되지 않습니다.전체 경로를 반환하거나 경로가 없으면 아무것도 반환하지 않습니다.1달러가 디렉터리가 될 것으로 예상하고 그렇지 않으면 실패할 수도 있지만, 직접 확인하는 것은 쉬운 일입니다.
말도 많고, 대답도 좀 늦었습니다.저는 오래된 RHEL4/5에 갇혀 있기 때문에 하나를 써야 합니다.절대 및 상대 링크를 처리하고 //, // 및 일부 dir/../ 항목을 단순화합니다.
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`
의 realpath
즉, BSD(또는 해당 OSX)에서는 사용할 수 없습니다.다음은 리눅스 저널의 다소 오래된(2009년) 기사에서 발췌한 간단한 레시피로, 휴대성이 매우 뛰어납니다.
function normpath() {
# Remove all /./ sequences.
local path=${1//\/.\//\/}
# Remove dir/.. sequences.
while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
path=${path/${BASH_REMATCH[0]}/}
done
echo $path
}
또한 이 변형은 경로가 필요하지 않습니다.
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_stemname 및 validate_path 함수도 포함되어 있습니다.여러 플랫폼에서 사용해 보고 개선을 지원합니다.
@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
저는 (재미를 위해) 최대한의 성능에 중점을 두고 이를 처리하기 위해 내장된 기능을 만들었습니다.심볼릭 링크를 해결하지 않으므로 기본적으로 다음과 같습니다.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
보다 약 10배 더 느리게 실행됩니다.realpath -sm
때, 하의주장호출때될로으나,abspath
보다 110배 이상 빠르게 실행됩니다.realpath -sm
대부분 매번 새로운 프로그램을 실행할 필요가 없기 때문에 내 컴퓨터에서.
파일 시스템을 건드리지 않고, 링크를 해결하지 않고, 외부 유틸리티를 사용하지 않고, 존재하거나 존재하지 않는 경로를 정규화하려면 Python에서 번역한 순수 Bash 함수가 여기 있습니다.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:-.}"
}
예:
new_path="$(normpath '/foo/bar/..')"
echo "${new_path}"
# /foo
normpath "relative/path/with trailing slashs////"
# relative/path/with trailing slashs
normpath "////a/../lot/././/mess////./here/./../"
# /lot/mess
normpath ""
# .
# (empty path resolved to dot)
개인적으로 파일을 조작하는 데 자주 사용되는 언어인 셸이 경로를 처리하는 기본 기능을 제공하지 않는 이유를 이해할 수 없습니다.파이썬에는 os.path나 pathlib와 같은 훌륭한 라이브러리가 있는데, 이 라이브러리는 파일 이름, 확장자, 기본 이름, 경로 세그먼트, 분할 또는 조인 경로를 추출하고, 절대 또는 정규화된 경로를 얻고, 경로 간의 관계를 결정하고, 많은 두뇌 없이 모든 것을 수행할 수 있는 모든 도구를 제공합니다.그리고 그들은 가장자리 케이스를 처리하고 신뢰할 수 있습니다.셸에서는 이러한 작업을 수행하기 위해 외부 실행 파일이라고 부르거나 매우 기본적이고 난해한 구문을 사용하여 휠을 재창조해야 합니다.
다음 세 가지를 모두 수행할 수 있는 솔루션이 필요했습니다.
- . 맥에서 일하세요.
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 "
loveborg의 훌륭한 파이썬 스니펫을 기반으로, 저는 다음과 같이 썼습니다.
#!/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)
파일이 없는 경우에도 작동합니다.파일이 들어 있는 디렉터리가 있어야 합니다.
이것이 오래된 질문이라는 것을 압니다.저는 여전히 대안을 제시하고 있습니다.최근에 같은 문제가 발생하여 이를 수행할 기존의 휴대용 명령어를 찾을 수 없었습니다.그래서 저는 트릭을 할 수 있는 기능을 포함한 다음과 같은 셸 스크립트를 작성했습니다.
#! /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
제시된 솔루션 중 저에게 맞는 솔루션이 없었기 때문에, 파일이 존재하지 않는 경우에 저는 제 아이디어를 구현했습니다.André Anjos의 솔루션은 .../../로 시작하는 경로가 잘못 해결되는 문제를 가지고 있었습니다.예를 들어 ....../a/b/가 a/b/가 되었습니다.
function normalize_rel_path(){
local path=$1
result=""
IFS='/' read -r -a array <<< "$path"
i=0
for (( idx=${#array[@]}-1 ; idx>=0 ; idx-- )) ; do
c="${array[idx]}"
if [ -z "$c" ] || [[ "$c" == "." ]];
then
continue
fi
if [[ "$c" == ".." ]]
then
i=$((i+1))
elif [ "$i" -gt "0" ];
then
i=$((i-1))
else
if [ -z "$result" ];
then
result=$c
else
result=$c/$result
fi
fi
done
while [ "$i" -gt "0" ]; do
i=$((i-1))
result="../"$result
done
unset IFS
echo $result
}
나는 오늘 당신이 사용할 수 있다는 것을 발견했습니다.stat
경로를 확인하는 명령입니다.
그래서 "~/Documents"와 같은 디렉토리의 경우:
다음을 실행할 수 있습니다.
stat -f %N ~/Documents
전체 경로를 가져오는 방법
/Users/me/Documents
심볼릭 링크의 경우 %Y 형식 옵션을 사용할 수 있습니다.
stat -f %Y example_symlink
다음과 같은 결과가 반환될 수 있습니다.
/usr/local/sbin/example_symlink
*NIX의 다른 버전에서는 포맷 옵션이 다를 수 있지만 OSX에서는 이러한 옵션이 작동했습니다.
다을사용간솔루션단한한을 사용한 간단한 node.js
:
#!/usr/bin/env node
process.stdout.write(require('path').resolve(process.argv[2]));
언급URL : https://stackoverflow.com/questions/284662/how-do-you-normalize-a-file-path-in-bash
'source' 카테고리의 다른 글
WPF의 진행률 표시줄에 있는 텍스트 (0) | 2023.05.12 |
---|---|
stackOverflow의 입력 태그와 같은 jQuery 자동 완성 태그 플러그인? (0) | 2023.05.12 |
Angular에서 확인 대화 상자를 만드는 쉬운 방법은 무엇입니까? (0) | 2023.05.12 |
Mongoose에서 Number 값을 증가시키려면 어떻게 해야 합니까? (0) | 2023.05.12 |
iOS용 동적 라이브러리를 구축하고 런타임에 로드할 수 있습니까? (0) | 2023.05.12 |