programing

현재 작업 디렉토리 대신 파일 위치를 기반으로하는 상대 경로

nasanasas 2020. 10. 12. 07:37
반응형

현재 작업 디렉토리 대신 파일 위치를 기반으로하는 상대 경로


주어진:

some.txt
dir
 |-cat.sh

내용이있는 cat.sh 사용 :

cat ../some.txt

그런 다음 ./cat.sh내부 dir실행 ./dir/cat.shdir그렇지 않은 것과 동일한 수준에서 실행 하는 동안 잘 작동합니다 . 나는 이것이 다른 작업 디렉토리 때문일 것으로 예상합니다. ../some.txt의 위치에 상대적인 경로를 만드는 것이 쉬웠 cat.sh습니까?


원하는 것은 스크립트의 절대 경로 (를 통해 사용 가능 ${BASH_SOURCE[0]})를 얻은 다음이를 사용하여 상위 디렉토리를 가져오고 cd스크립트 시작 부분 에이 경로를 가져 오는 것 입니다.

#!/bin/bash
parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )

cd "$parent_path"
cat ../some.text

이렇게하면 쉘 스크립트가 호출되는 위치와 독립적으로 작동합니다. 실행할 때마다 ./cat.sh내부 에서 실행하는 것처럼 보입니다 dir.

이 스크립트는 스크립트를 직접 호출하는 경우에만 작동합니다 (즉, 심볼릭 링크를 통하지 않음). 그렇지 않으면 스크립트의 현재 위치를 찾는 것이 조금 더 까다로워집니다.


@Martin Konecny의 대답정답을 제공하지만 그가 언급했듯이 실제 스크립트가 다른 디렉토리 에있는 심볼릭 링크를 통해 호출되지 않는 경우에만 작동합니다 .

이 답변은 해당 경우를 다룹니다 . 스크립트가 심볼릭 링크 또는 심볼 체인을 통해 호출 될 때도 작동 하는 솔루션 :


Linux / GNUreadlink 솔루션 :

스크립트가 Linux 에서만 실행되어야 하거나 GNUreadlink 가에 있음을 알고 있는 경우 $PATH,를 사용 readlink -f하여 심볼릭 링크를 최종 대상 으로 편리하게 확인합니다 .

 scriptDir=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")

GNU readlink에는 심볼릭 링크를 최종 대상의 전체 경로 -f(( --canonicalize), -e( --canonicalize-existing) 및 -m( --canonicalize-missing)) 로 확인하기위한 3 개의 관련 옵션이 있습니다 man readlink.
이 시나리오에는 정의에 의한 대상이 존재하므로 세 가지 옵션 중 하나를 사용할 수 있습니다. -f가장 잘 알려진 것이기 때문에 여기를 선택 했습니다.


다중 (유닉스 유사) 플랫폼 솔루션 ( POSIX 전용 유틸리티 세트 가있는 플랫폼 포함 ) :

스크립트가 다음과 같은 플랫폼에서 실행되어야하는 경우 :

  • readlink유틸리티를하지만, 부족 -f, 예를 - (궁극적 인 목표에 심볼릭 링크를 해결하는 GNU의 의미에서) 옵션을 맥 OS .

    • macOS는 이전 버전 의 BSD 구현을 사용합니다 readlink. 참고 FreeBSD의 / PC-BSD의 최신 버전은 어떻게 지원 -f.
  • 심지어 가지고 있지 않다 readlink 없지만 POSIX 호환 유틸리티 (예 : HP-UX , @Charles Duffy)가 있습니다.

https://stackoverflow.com/a/1116890/45375 에서 영감을 얻은 다음 솔루션 은 주어진 심볼릭 링크를 루프의 최종 대상으로 확인하는 헬퍼 셸 함수를rreadlink() 정의 합니다.이 함수는 사실상 POSIX 호환 구현입니다. 암소 비슷한 일종의 영양readlink-e옵션 이며, 받는 사람과 비슷한 -f궁극적 인 목표는해야한다는 것을 제외하고, 옵션 존재한다 .

참고 :이 함수는 bash함수이며 POSIX 호환 옵션이있는 POSIX 유틸리티 만 사용된다는 점에서 POSIX 호환입니다. 이 기능의 버전은 자체가 POSIX 호환 쉘 코드 (대한 작성 /bin/sh)를 참조하십시오 여기 .

  • readlink사용 가능한 경우 (옵션없이) 사용됩니다. 대부분의 최신 플랫폼에서 마찬가지입니다.

  • 그렇지 않으면의 출력 ls -l이 구문 분석되어 심볼릭 링크의 대상을 결정하는 유일한 POSIX 호환 방법입니다.
    주의 사항 : 파일 이름이나 경로에 리터럴 하위 문자열이 포함되어 있으면 중단됩니다 ->. 그러나 가능성은 낮습니다.
    (부족한 플랫폼은readlink 은 여전히 ​​심볼릭 링크를 해결하기 위해 POSIX가 아닌 다른 방법을 제공 할 수 있습니다. 예를 들어 @Charles Duffy는 기본 문자로 char 형식을 find지원하는 HP-UX의 유틸리티를 언급합니다. 간결성을 위해이 기능은 시도하지 않습니다. 그러한 경우를 감지하십시오.)%l-printf

  • 설치 가능한 유틸리티 (스크립트) 형식의 아래 함수 (추가 기능 포함)는 npm 레지스트리에서 찾을 수 있습니다 . Linux 및 macOS에서는 ; 다른 플랫폼 (이 있다고 가정 )에서는 수동 설치 지침을 따르십시오 . rreadlink[sudo] npm install -g rreadlinkbash

인수가 심볼릭 링크이면 최종 대상의 표준 경로가 반환됩니다. 그렇지 않으면 인수 자체의 표준 경로가 반환됩니다.

#!/usr/bin/env bash

# Helper function.
rreadlink() ( # execute function in a *subshell* to localize the effect of `cd`, ...

  local target=$1 fname targetDir readlinkexe=$(command -v readlink) CDPATH= 

  # Since we'll be using `command` below for a predictable execution
  # environment, we make sure that it has its original meaning.
  { \unalias command; \unset -f command; } &>/dev/null

  while :; do # Resolve potential symlinks until the ultimate target is found.
      [[ -L $target || -e $target ]] || { command printf '%s\n' "$FUNCNAME: ERROR: '$target' does not exist." >&2; return 1; }
      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
      fname=$(command basename -- "$target") # Extract filename.
      [[ $fname == '/' ]] && fname='' # !! curiously, `basename /` returns '/'
      if [[ -L $fname ]]; then
        # Extract [next] target path, which is defined
        # relative to the symlink's own directory.
        if [[ -n $readlinkexe ]]; then # Use `readlink`.
          target=$("$readlinkexe" -- "$fname")
        else # `readlink` utility not available.
          # Parse `ls -l` output, which, unfortunately, is the only POSIX-compliant 
          # way to determine a symlink's target. Hypothetically, this can break with
          # filenames containig literal ' -> ' and embedded newlines.
          target=$(command ls -l -- "$fname")
          target=${target#* -> }
        fi
        continue # Resolve [next] symlink target.
      fi
      break # Ultimate target reached.
  done
  targetDir=$(command pwd -P) # Get canonical dir. path
  # Output the ultimate target's canonical path.
  # Note that we manually resolve paths ending in /. and /.. to make sure we
  # have a normalized path.
  if [[ $fname == '.' ]]; then
    command printf '%s\n' "${targetDir%/}"
  elif  [[ $fname == '..' ]]; then
    # Caveat: something like /var/.. will resolve to /private (assuming
    # /var@ -> /private/var), i.e. the '..' is applied AFTER canonicalization.
    command printf '%s\n' "$(command dirname -- "${targetDir}")"
  else
    command printf '%s\n' "${targetDir%/}/$fname"
  fi
)

# Determine ultimate script dir. using the helper function.
# Note that the helper function returns a canonical path.
scriptDir=$(dirname -- "$(rreadlink "$BASH_SOURCE")")

한 줄이면 괜찮습니다.

cat "`dirname $0`"/../some.txt

참고 URL : https://stackoverflow.com/questions/24112727/relative-paths-based-on-file-location-instead-of-current-working-directory

반응형