문제

Linux에서 readlink 유틸리티는 옵션을 수락합니다 -f 추가 링크를 따릅니다. 이것은 MAC 및 BSD 기반 시스템에서 작동하지 않는 것 같습니다. 동등한 것은 무엇입니까?

디버그 정보는 다음과 같습니다.

$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
도움이 되었습니까?

해결책

readlink -f 두 가지를 수행합니다.

  1. 실제 파일을 찾을 때까지 일련의 Symlinks를 따라 반복됩니다.
  2. 해당 파일을 반환합니다 정식화 이름 —IE, 절대 경로 이름.

원한다면 바닐라 읽기 링크 동작을 사용하여 같은 것을 달성하는 쉘 스크립트 만 빌드 할 수 있습니다. 여기 예입니다. 분명히 당신은 전화하고 싶은 스크립트에 이것을 삽입 할 수 있습니다. readlink -f

#!/bin/sh

TARGET_FILE=$1

cd `dirname $TARGET_FILE`
TARGET_FILE=`basename $TARGET_FILE`

# Iterate down a (possible) chain of symlinks
while [ -L "$TARGET_FILE" ]
do
    TARGET_FILE=`readlink $TARGET_FILE`
    cd `dirname $TARGET_FILE`
    TARGET_FILE=`basename $TARGET_FILE`
done

# Compute the canonicalized name by finding the physical path 
# for the directory we're in and appending the target file.
PHYS_DIR=`pwd -P`
RESULT=$PHYS_DIR/$TARGET_FILE
echo $RESULT

여기에는 오류 처리가 포함되지 않습니다. 특히 중요한 것은 심볼릭 사이클을 감지하지 않습니다. 이렇게하는 간단한 방법은 루프를 돌아 다니는 횟수를 계산하고 1,000과 같이 불가능한 많은 수를 쳤다면 실패하는 것입니다.

사용하려면 편집 pwd -P 대신에 $PWD.

이 스크립트는처럼 호출 될 것으로 예상됩니다 ./script_name filename, 아니요 -f, 변화 $1 에게 $2 당신이 함께 사용할 수 있기를 원한다면 -f filename gnu readlink처럼.

다른 팁

Macports와 Homebrew는 a Coreutils 패키지 포함 greadlink (GNU 읽기 링크). mackb.com의 Michael Kallweitt Post에 대한 크레딧.

brew install coreutils

greadlink -f file.txt

당신은 관심이있을 수 있습니다 realpath(3), 또는 파이썬 os.path.realpath. 두 사람은 정확히 같지 않습니다. C 라이브러리 호출은 중간 경로 구성 요소가 존재하지만 파이썬 버전은 그렇지 않습니다.

$ pwd
/tmp/foo
$ ls -l
total 16
-rw-r--r--  1 miles    wheel  0 Jul 11 21:08 a
lrwxr-xr-x  1 miles    wheel  1 Jul 11 20:49 b -> a
lrwxr-xr-x  1 miles    wheel  1 Jul 11 20:49 c -> b
$ python -c 'import os,sys;print(os.path.realpath(sys.argv[1]))' c
/private/tmp/foo/a

나는 당신이 다른 스크립팅 언어보다 더 가벼운 것을 선호한다고 말했지만 바이너리를 컴파일 할 수없는 경우 Python과 CTypes (Mac OS X 10.5에서 사용 가능)를 사용하여 라이브러리 전화를 랩핑 할 수 있습니다.

#!/usr/bin/python

import ctypes, sys

libc = ctypes.CDLL('libc.dylib')
libc.realpath.restype = ctypes.c_char_p
libc.__error.restype = ctypes.POINTER(ctypes.c_int)
libc.strerror.restype = ctypes.c_char_p

def realpath(path):
    buffer = ctypes.create_string_buffer(1024) # PATH_MAX
    if libc.realpath(path, buffer):
        return buffer.value
    else:
        errno = libc.__error().contents.value
        raise OSError(errno, "%s: %s" % (libc.strerror(errno), buffer.value))

if __name__ == '__main__':
    print realpath(sys.argv[1])

아이러니 하게도이 스크립트의 C 버전은 더 짧아야합니다. :)

나는 또 다른 구현으로 쌓는 것을 싫어하지만 a) 휴대용 순수한 쉘 구현, 및 b) 단위 테스트 범위, 이와 같은 것에 대한 에지 사례의 수는 사소한.

보다 Github에 대한 나의 프로젝트 테스트 및 전체 코드. 다음은 구현의 시놉시스입니다.

Keith Smith가 지적한 것처럼 readlink -f 두 가지를 수행합니다. 1) Symlinks를 재귀 적으로 해결하고 2) 결과를 정식화하여 다음과 같이합니다.

realpath() {
    canonicalize_path "$(resolve_symlinks "$1")"
}

먼저 Symlink Resolver 구현 :

resolve_symlinks() {
    local dir_context path
    path=$(readlink -- "$1")
    if [ $? -eq 0 ]; then
        dir_context=$(dirname -- "$1")
        resolve_symlinks "$(_prepend_path_if_relative "$dir_context" "$path")"
    else
        printf '%s\n' "$1"
    fi
}

_prepend_path_if_relative() {
    case "$2" in
        /* ) printf '%s\n' "$2" ;;
         * ) printf '%s\n' "$1/$2" ;;
    esac 
}

이것은 약간 단순화 된 버전입니다 전체 구현. 전체 구현은 Symlink 사이클에 대한 작은 검사를 추가합니다., 출력을 약간 마사지 할뿐만 아니라.

마지막으로, 경로를 정식화하는 기능 :

canonicalize_path() {
    if [ -d "$1" ]; then
        _canonicalize_dir_path "$1"
    else
        _canonicalize_file_path "$1"
    fi
}   

_canonicalize_dir_path() {
    (cd "$1" 2>/dev/null && pwd -P) 
}           

_canonicalize_file_path() {
    local dir file
    dir=$(dirname -- "$1")
    file=$(basename -- "$1")
    (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
}

그게 다야. 스크립트에 붙여 넣을만큼 간단하지만 사용 사례에 대한 단위 테스트가없는 코드에 의존하기에 충분히 까다로울 정도로 까다 롭습니다.

  1. 홈브류를 설치하십시오
  2. "Brew Install Coreutils"실행
  3. "greadlink -f path"실행

GreadLink는 -f를 구현하는 GNU 읽기 링크입니다. Macports 또는 다른 사람들도 사용할 수 있습니다. 저는 홈브류를 선호합니다.

외부 의존성없이 거의 모든 곳에서 작동 할 수있는 Perl의 간단한 1 라이너 :

perl -MCwd -e 'print Cwd::abs_path shift' ~/non-absolute/file

Symlinks의 Dereference.

스크립트의 사용은 다음과 같습니다.

readlinkf(){ perl -MCwd -e 'print Cwd::abs_path shift' "$1";}
ABSPATH="$(readlinkf ./non-absolute/file)"

나는 RealPath라는 스크립트를 개인적으로 만들었습니다.

#!/usr/bin/env python
import os.sys
print os.path.realpath(sys.argv[1])

이건 어때?

function readlink() {
  DIR="${1%/*}"
  (cd "$DIR" && echo "$(pwd -P)")
}

freebsd 그리고 OSX 버전이 있습니다 statNetBsd에서 파생되었습니다.

형식 스위치로 출력을 조정할 수 있습니다 (위의 링크의 수동 페이지 참조).

%  cd  /service
%  ls -tal 
drwxr-xr-x 22 root wheel 27 Aug 25 10:41 ..
drwx------  3 root wheel  8 Jun 30 13:59 .s6-svscan
drwxr-xr-x  3 root wheel  5 Jun 30 13:34 .
lrwxr-xr-x  1 root wheel 30 Dec 13 2013 clockspeed-adjust -> /var/service/clockspeed-adjust
lrwxr-xr-x  1 root wheel 29 Dec 13 2013 clockspeed-speed -> /var/service/clockspeed-speed
% stat -f%R  clockspeed-adjust
/var/service/clockspeed-adjust
% stat -f%Y  clockspeed-adjust
/var/service/clockspeed-adjust

일부 OS X 버전 stat 부족할 수 있습니다 -f%R 형식 옵션. 이 경우 -stat -f%Y 충분할 수 있습니다. 그만큼 -f%Y 옵션은 Symlink의 대상을 표시하는 반면 -f%R 파일에 해당하는 절대 PathName을 보여줍니다.

편집하다:

Perl을 사용할 수있는 경우 (Darwin/OS X는 최근의 정점과 함께 설치됩니다. perl) 그 다음에:

perl -MCwd=abs_path -le 'print abs_path readlink(shift);' linkedfile.txt

작동합니다.

다음은 작동 해야하는 휴대용 쉘 기능입니다. 어느 Bourne 비슷한 쉘. 상대 경로 구두점을 해결할 것입니다. 그리고 Derefenference 기호 링크.

어떤 이유로 든 realPath (1) 명령이 없거나 readLink (1)가없는 경우 별명이 될 수 있습니다.

which realpath || alias realpath='real_path'

즐기다:

real_path () {
  OIFS=$IFS
  IFS='/'
  for I in $1
  do
    # Resolve relative path punctuation.
    if [ "$I" = "." ] || [ -z "$I" ]
      then continue
    elif [ "$I" = ".." ]
      then FOO="${FOO%%/${FOO##*/}}"
           continue
      else FOO="${FOO}/${I}"
    fi

    ## Resolve symbolic links
    if [ -h "$FOO" ]
    then
    IFS=$OIFS
    set `ls -l "$FOO"`
    while shift ;
    do
      if [ "$1" = "->" ]
        then FOO=$2
             shift $#
             break
      fi
    done
    IFS='/'
    fi
  done
  IFS=$OIFS
  echo "$FOO"
}

또한 여기에 관심이있는 경우 100% Pure Shell Code에서 Basename 및 Dirname을 구현하는 방법이 있습니다.

## http://www.opengroup.org/onlinepubs/000095399/functions/dirname.html
# the dir name excludes the least portion behind the last slash.
dir_name () {
  echo "${1%/*}"
}

## http://www.opengroup.org/onlinepubs/000095399/functions/basename.html
# the base name excludes the greatest portion in front of the last slash.
base_name () {
  echo "${1##*/}"
}

내 Google 사이트 에서이 쉘 코드의 업데이트 된 버전을 찾을 수 있습니다. http://sites.google.com/site/jdisnard/realpath

편집 :이 코드는 2- 클라스 (FreeBSD Style) 라이센스의 조건에 따라 라이센스가 부여됩니다. 위의 하이퍼 링크를 내 사이트로 따라 사용하여 라이센스 사본을 찾을 수 있습니다.

이 문제를 해결하고 Mac에서 readlink의 기능을 활성화하는 가장 쉬운 방법은 Homebrew가 설치되거나 freebsd가 'Coreutils'패키지를 설치하는 것입니다. 특정 Linux 배포판 및 기타 POSIX OS에도 필요할 수도 있습니다.

예를 들어 FreeBSD 11에서 호출하여 설치했습니다.

# pkg install coreutils

Homebrew가있는 MacOS에서는 다음과 같습니다.

$ brew install coreutils

다른 답변이 왜 그렇게 복잡한 지 잘 모르겠습니다. 그게 전부입니다. 파일은 다른 장소에 있지 않으며 아직 설치되지 않았습니다.

업데이트를 시작하십시오

이것은 우리가 무료로 사용하기 위해 Bash 4 라이브러리를 구성하는 빈번한 문제입니다. RealPath-lib. 이것은 에뮬레이션하도록 설계되었습니다 readlink -f 기본적으로 주어진 UNIX 시스템에 대해 작동하고 (2) readlink -f 설치된 경우 (그러나 이것은 필요하지 않습니다). 또한, 깊고 부러진 상징 링크 및 원형 참조를 조사, 식별 및 풀어주는 데 사용될 수 있으므로 깊은 네트워크 물리적 또는 상징적 디렉토리 및 파일 문제를 진단하는 데 유용한 도구가 될 수 있습니다. 찾을 수 있습니다 github.com 또는 bitbucket.org.

최종 업데이트

Bash 외에는 아무것도 의존하지 않는 또 다른 매우 작고 효율적인 솔루션이 다음과 같습니다.

function get_realpath() {

    [[ ! -f "$1" ]] && return 1 # failure : file does not exist.
    [[ -n "$no_symlinks" ]] && local pwdp='pwd -P' || local pwdp='pwd' # do symlinks.
    echo "$( cd "$( echo "${1%/*}" )" 2>/dev/null; $pwdp )"/"${1##*/}" # echo result.
    return 0 # success

}

여기에는 환경 설정도 포함됩니다 no_symlinks 이는 물리적 시스템에 대한 Symlinks를 해결할 수있는 기능을 제공합니다. 하는 한 no_symlinks 무언가로 설정됩니다 no_symlinks='on' 그런 다음 Symlinks가 물리적 시스템으로 해결됩니다. 그렇지 않으면 적용됩니다 (기본 설정).

이것은 배쉬를 제공하는 모든 시스템에서 작동하며 테스트 목적으로 Bash 호환 종료 코드를 반환합니다.

이미 많은 답변이 있지만 아무도 저에게 적합한 사람이 없습니다 ... 그래서 이것이 제가 지금 사용하고있는 것입니다.

readlink_f() {
  local target="$1"
  [ -f "$target" ] || return 1 #no nofile

  while [ -L "$target" ]; do
    target="$(readlink "$target")" 
  done
  echo "$(cd "$(dirname "$target")"; pwd -P)/$target"
}

결코 늦지 않는 것보다 늦었습니다. Fedora 스크립트가 MAC에서 작업하지 않았기 때문에이 문제를 구체적으로 개발하도록 동기를 부여했습니다. 문제는 종속성과 배쉬입니다. Mac은 그것들을 가지고 있지 않거나 그렇게한다면 종종 다른 곳에 있습니다 (다른 경로). 크로스 플랫폼 배쉬 스크립트의 종속성 경로 조작은 최상의 두통이자 최악의 보안 위험이므로 가능한 경우 사용을 피하는 것이 가장 좋습니다.

아래의 get_realpath () 함수는 단순하고 하시 중심이며 종속성이 필요하지 않습니다. 나는 bash buildins 만 사용합니다 에코 그리고 CD. 또한 모든 단계에서 모든 것이 테스트되고 오류 조건을 반환하기 때문에 상당히 안전합니다.

Symlinks를 따르고 싶지 않다면 세트 -p 스크립트의 앞면에 있지만 그렇지 않으면 CD 기본적으로 Symlinks를 해결해야합니다. {절대 | 상대 | Symlink | 로컬} 파일의 절대 경로를 반환합니다. 지금까지 우리는 그것에 아무런 문제가 없었습니다.

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와 결합 할 수 있습니다. 이것들은 Github 저장소에서 찾을 수 있습니다 RealPath-lib (전체 공개 - 이것은 우리 제품이지만 제한없이 커뮤니티에 무료로 제공합니다). 또한 교육 도구 역할을 할 수도 있습니다. 잘 문서화되어 있습니다.

우리는 소위 'Modern Bash'관행을 적용하기 위해 최선을 다했지만 Bash는 큰 주제이며 항상 개선의 여지가있을 것이라고 확신합니다. Bash 4+가 필요하지만 여전히 주변에있는 경우 이전 버전과 함께 작동하도록 만들 수 있습니다.

Platform-Indendent 도이 R- 온 라이너가 될 것입니다

readlink(){ RScript -e "cat(normalizePath(commandArgs(T)[1]))" "$1";}

실제로 모방합니다 readlink -f <path>, $ 1 대신 $ 2를 사용해야합니다.

내 작업은 MACO뿐만 아니라 BSD가 아닌 Linux를 가진 사람들이 사용하기 때문에 빌드 스크립트에서 이러한 별칭을 사용하기로 결정했습니다 (sed 비슷한 문제가 있으므로 포함) :

##
# If you're running macOS, use homebrew to install greadlink/gsed first:
#   brew install coreutils
#
# Example use:
#   # Gets the directory of the currently running script
#   dotfilesDir=$(dirname "$(globalReadlink -fm "$0")")
#   alias al='pico ${dotfilesDir}/aliases.local'
##

function globalReadlink () {
  # Use greadlink if on macOS; otherwise use normal readlink
  if [[ $OSTYPE == darwin* ]]; then
    greadlink "$@"
  else
    readlink "$@"
  fi
}

function globalSed () {
  # Use gsed if on macOS; otherwise use normal sed
  if [[ $OSTYPE == darwin* ]]; then
    gsed "$@"
  else
    sed "$@"
  fi
}

자동으로 설치하기 위해 추가 할 수있는 선택적 점검 고향 + Coreutils 종속성 :

if [[ "$OSTYPE" == "darwin"* ]]; then
  # Install brew if needed
  if [ -z "$(which brew)" ]; then 
    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"; 
  fi
  # Check for coreutils
  if [ -z "$(brew ls coreutils)" ]; then
    brew install coreutils
  fi
fi

나는 진정으로 "글로벌"이라고 생각하지만 다른 사람들을 확인해야하지만 아마도 80/20 마크에 가까워 질 것입니다.

나는 썼다 OS X의 실제 유틸리티 동일한 결과를 제공 할 수 있습니다 readlink -f.


예는 다음과 같습니다.

(jalcazar@mac tmp)$ ls -l a
lrwxrwxrwx 1 jalcazar jalcazar 11  8月 25 19:29 a -> /etc/passwd

(jalcazar@mac tmp)$ realpath a
/etc/passwd


MacPorts를 사용하는 경우 다음 명령으로 설치할 수 있습니다. sudo port selfupdate && sudo port install realpath.

설명

Coreutils는 a brew GNU/Linux Core 유틸리티를 설치하는 패키지는 Mac OSX 구현에 해당하여 사용할 수 있습니다.

Mac OSX 시스템에서 Linux Coreutils ( "Core Utilities")와 비슷한 것처럼 보이지만 어떤 방식 (예 : 다른 플래그를 가진)과 다릅니다.

이는 이러한 도구의 Mac OSX 구현이 다르기 때문입니다. 원래 GNU/Linux와 같은 동작을 얻으려면 coreutils 패키지를 통해 brew 패키지 관리 시스템.

이로 인해 접두사가 접두사에 해당하는 핵심 유틸리티를 설치합니다 g. 예를 들어 readlink, 당신은 해당하는 것을 찾을 수 있습니다 greadlink 프로그램.

만들기 위해서 readlink GNU처럼 수행하십시오 readlink (greadlink) 구현, Coreutils를 설치 한 후 간단한 별칭을 만들 수 있습니다.

구현

  1. Brew를 설치하십시오

지침을 따르십시오 https://brew.sh/

  1. Coreutils 패키지를 설치하십시오

brew install coreutils

  1. 별칭을 만듭니다

별명을 ~/.bashrc, ~/.bash_profile에 배치 할 수 있습니다. 나는 개인적으로 ~/.bashrc에 내 것을 보관합니다

alias readlink=greadlink

GMV, GDU, GDF 등과 같은 다른 코어 루틸에 대해 유사한 별칭을 만들 수 있습니다. 그러나 MAC 기계의 GNU 동작은 네이티브 코어 루틸과 협력하는 데 사용되는 다른 사람들에게 혼란스러워하거나 MAC 시스템에서 예기치 않은 방식으로 행동 할 수 있음을 조심하십시오.

이것이 제가 사용하는 것입니다.

stat -f %N $your_path

읽기 링크 경로는 내 시스템과 귀하의 사이에 다릅니다. 전체 경로를 지정해보십시오.

/sw/sbin/readlink -f

Perl은 readlink 함수가 있습니다 (예 : Perl에서 기호 링크를 어떻게 복사합니까?). 이것은 OS X를 포함하여 대부분의 플랫폼에서 작동합니다.

perl -e "print readlink '/path/to/link'"

예를 들어:

$ mkdir -p a/b/c
$ ln -s a/b/c x
$ perl -e "print readlink 'x'"
a/b/c

@Keith Smith의 답변은 무한 루프를 제공합니다.

다음은 Sunos에서만 사용하는 내 대답입니다 (Sunos Miss 많은 Posix 및 GNU 명령).

$ 경로 디렉토리 중 하나에 넣어야하는 스크립트 파일입니다.

#!/bin/sh
! (($#)) && echo -e "ERROR: readlink <link to analyze>" 1>&2 && exit 99

link="$1"
while [ -L "$link" ]; do
  lastLink="$link"
  link=$(/bin/ls -ldq "$link")
  link="${link##* -> }"
  link=$(realpath "$link")
  [ "$link" == "$lastlink" ] && echo -e "ERROR: link loop detected on $link" 1>&2 && break
done

echo "$link"
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top