programing

OS X에서 Bash 스크립트 절대 경로

testmans 2023. 4. 20. 20:09
반응형

OS X에서 Bash 스크립트 절대 경로

OS X에서 현재 실행 중인 스크립트에 대한 절대 경로를 얻으려고 합니다.

많은 댓글들이 올라오고 있어요.readlink -f $0 OS의 ,, OS X의 ,는readlinkBSD와 동일하지만 동작하지 않습니다(GNU 버전에서는 동작합니다).

바로 사용할 수 있는 솔루션이 있습니까?

다음의 3개의 간단한 스텝으로, 이 문제와 그 외의 OS X의 많은 문제를 해결할 수 있습니다.

  1. 홈브루 설치
  2. brew install coreutils
  3. grealpath .

3)은 (3)으로 수 realpath (2) (2) 「」를 참조해 주세요.

게 요.realpath()C 기능은 작동하지만 명령줄에서 사용할 수 있는 기능이 없습니다.여기 간단한 대체 방법이 있습니다.

#!/bin/bash

realpath() {
    [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

realpath "$0"

할 수 ./ 않은 상대 에 ' 경로'가 붙습니다$PWD★★★★★★★★★★★★★★★★★.#././$1.

가지 이 좀 하다는 것을 .
특히 심볼릭 링크의 여러 단계를 해결하지 않고 매우 "Bash-y"입니다.

번째 에서는 " 스크립트를 명시적으로 의 BSD와 비GNU에 하고 있습니다.readlink.

여기에서는 임의의 수의 심볼릭링크를 해결하는 합리적인 이식성(bash를 'sh'와 대시로 체크)을 시도합니다.경로 내의 공백 공간에서도 동작합니다.

편집되어 되어 있습니다.local 솔루션입니다.POSIX를 소개합니다.인라인 함수가 아닌 서브셸 함수로 변경하여 변수 범위 지정에 대응하도록 편집했습니다.집하편

#!/bin/sh
realpath() (
  OURPWD=$PWD
  cd "$(dirname "$1")"
  LINK=$(readlink "$(basename "$1")")
  while [ "$LINK" ]; do
    cd "$(dirname "$LINK")"
    LINK=$(readlink "$(basename "$1")")
  done
  REALPATH="$PWD/$(basename "$1")"
  cd "$OURPWD"
  echo "$REALPATH"
)
realpath "$@"

그것이 누군가에게 도움이 될 수 있기를 바랍니다.

Python 솔루션의 보다 명령줄 친화적인 변형:

python -c 'import os, sys; print(os.path.realpath(sys.argv[1]))' ./my/path

다른 사람들이 지적한 것처럼 진정한 경로가 있기 때문에:

// realpath.c
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char* argv[])
{
  if (argc > 1) {
    for (int argIter = 1; argIter < argc; ++argIter) {
      char *resolved_path_buffer = NULL;
      char *result = realpath(argv[argIter], resolved_path_buffer);

      puts(result);

      if (result != NULL) {
        free(result);
      }
    }
  }

  return 0;
}

파일 만들기:

#Makefile
OBJ = realpath.o

%.o: %.c
      $(CC) -c -o $@ $< $(CFLAGS)

realpath: $(OBJ)
      gcc -o $@ $^ $(CFLAGS)

음음음음 with with with with with with with with로 편찬해보세요.make하다
ln -s $(pwd)/realpath /usr/local/bin/realpath

모든 답변을 확인했지만, Jason S Jul 14'16의 IMHO를 3:12에 놓쳐 코멘트 필드를 떠났습니다.

나 같은 사람이 답을 확인하는 경향이 있어서 모든 코멘트를 일일이 살펴볼 시간이 없는 경우:

$( cd "$(dirname "$0")" ; pwd -P )

도움말:

NAME
     pwd -- return working directory name

SYNOPSIS
     pwd [-L | -P]

DESCRIPTION
     The pwd utility writes the absolute pathname of the current working
     directory to the standard output.

     Some shells may provide a builtin pwd command which is similar or identi-
     cal to this utility.  Consult the builtin(1) manual page.

     The options are as follows:

     -L      Display the logical current working directory.

     -P      Display the physical current working directory (all symbolic
             links resolved).
abs_path () {    
   echo "$(cd $(dirname "$1");pwd)/$(basename "$1")"
}

dirname는, 을 「(디렉토리명칭은 「Directory Name」입니다./path/to/file discriptions./path/to.

cd /path/to; pwd는 패스가 절대임을 보증합니다.

basename에서는 파일 만 알 수 ./path/to/file 말해 네.file.

시스템 프로비저닝 스크립트에서 사용할 솔루션을 찾고 있었습니다.즉, Homebrew가 설치되기도 전에 실행되었습니다.적절한 솔루션이 없기 때문에 작업을 크로스 플랫폼 언어로 오프로드합니다.예를 들어 Perl)로 오프로드합니다.

script_abspath=$(perl -e 'use Cwd "abs_path"; print abs_path(@ARGV[0])' -- "$0")

대부분의 경우 실제로 필요한 것은 다음과 같은 디렉토리입니다.

here=$(perl -e 'use File::Basename; use Cwd "abs_path"; print dirname(abs_path(@ARGV[0]));' -- "$0")

Python을 사용하면 다음과 같이 얻을 수 있습니다.

#!/usr/bin/env python
import os
import sys

print(os.path.realpath(sys.argv[1]))

Mac OS X의 realpath

realpath() {
    path=`eval echo "$1"`
    folder=$(dirname "$path")
    echo $(cd "$folder"; pwd)/$(basename "$path"); 
}

관련 경로의 예:

realpath "../scripts/test.sh"

홈 폴더의 예

realpath "~/Test/../Test/scripts/test.sh"

위에서 보시는 것처럼 6개월 전에 이걸 찍었습니다.나는 다시 비슷한 것이 필요하다는 것을 알게 될 때까지 그것에 대해 까맣게 잊고 있었다.저는 그것이 얼마나 초보적인지 보고 완전히 놀랐습니다.저는 약 1년 동안 집중적으로 코드를 익혔지만, 상황이 최악일 때는 아무것도 배우지 못한 것 같은 기분이 들 때가 종종 있습니다.

저는 위의 '해결책'을 없애고 싶지만, 지난 몇 달 동안 제가 얼마나 많은 것을 배웠는지 보여주는 기록인 것이 정말 좋습니다.

하지만 나는 주제에서 벗어났군.어젯밤에 앉아서 다 풀었어.코멘트의 설명은 충분합니다.제가 계속 작업하고 있는 복사본을 추적하려면 다음 요점을 따르십시오.이 정도면 충분할 거야

#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.

## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
## dereference symbolic links (ala 'readlink') until the originating file
## is found. This is effectively the same function provided in stdlib.h as
## 'realpath' and on the command line in GNU 'readlink -f'.

## Neither of these tools, however, are particularly accessible on the many
## systems that do not have the GNU implementation of readlink, nor ship
## with a system compiler (not to mention the requisite knowledge of C).

## This script is written with portability and (to the extent possible, speed)
## in mind, hence the use of printf for echo and case statements where they
## can be substituded for test, though I've had to scale back a bit on that.

## It is (to the best of my knowledge) written in standard POSIX shell, and
## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
## issues with it, though I'm not sure why; so probably best to avoid for now.

## Particularly useful (in fact, the reason I wrote this) is the fact that
## it can be used within a shell script to find the path of the script itself.
## (I am sure the shell knows this already; but most likely for the sake of
## security it is not made readily available. The implementation of "$0"
## specificies that the $0 must be the location of **last** symbolic link in
## a chain, or wherever it resides in the path.) This can be used for some
## ...interesting things, like self-duplicating and self-modifiying scripts.

## Currently supported are three errors: whether the file specified exists
## (ala ENOENT), whether its target exists/is accessible; and the special
## case of when a sybolic link references itself "foo -> foo": a common error
## for beginners, since 'ln' does not produce an error if the order of link
## and target are reversed on the command line. (See POSIX signal ELOOP.)

## It would probably be rather simple to write to use this as a basis for
## a pure shell implementation of the 'symlinks' util included with Linux.

## As an aside, the amount of code below **completely** belies the amount
## effort it took to get this right -- but I guess that's coding for you.

##===-------------------------------------------------------------------===##

for argv; do :; done # Last parameter on command line, for options parsing.

## Error messages. Use functions so that we can sub in when the error occurs.

recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
errnoent(){ printf "No such file: "$@"\n" ;} # Borrow a horrible signal name.

# Probably best not to install as 'pathfull', if you can avoid it.

pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"

## 'test and 'ls' report different status for bad symlinks, so we use this.

 if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null;  then
    errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ];   then
    recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -z "$link" ]; then
    dangling 1>&2; exit 1; fi
 fi

## Not a link, but there might be one in the path, so 'cd' and 'pwd'.

 if [ -z "$link" ]; then if [ "$(dirname "$@" | cut -c1)" = '/' ]; then
   printf "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\n"; fi; exit 0
 fi

## Walk the symlinks back to the origin. Calls itself recursivly as needed.

 while [ "$link" ]; do
   cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
   case "$newlink" in
    "$link") dangling 1>&2 && exit 1                                       ;;
         '') printf "$(pwd)/$(basename "$link")\n"; exit 0                 ;;
          *) link="$newlink" && pathfull "$link"                           ;;
   esac
 done
 printf "$(pwd)/$(basename "$newlink")\n"
}

## Demo. Install somewhere deep in the filesystem, then symlink somewhere 
## else, symlink again (maybe with a different name) elsewhere, and link
## back into the directory you started in (or something.) The absolute path
## of the script will always be reported in the usage, along with "$0".

if [ -z "$argv" ]; then scriptname="$(pathfull "$0")"

# Yay ANSI l33t codes! Fancy.
 printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
 printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         \033[4m$scriptname\033[24m\n\n        "
 printf " From within an invocation of a script, locate the script's "
 printf "own file\n         (no matter where it has been linked or "
 printf "from where it is being called).\n\n"

else pathfull "$@"
fi

하게 처리할 수 있는 를 사용하는 입니다.realpath 필요하니까.이것이 필요하기 때문에brew install coreutils그 단계를 자동화했습니다.을 사용하다

#!/usr/bin/env bash

set -e

if ! which realpath >&/dev/null; then
  if ! which brew >&/dev/null; then
    msg="ERROR: This script requires brew. See https://brew.sh for installation instructions."
    echo "$(tput setaf 1)$msg$(tput sgr0)" >&2
    exit 1
  fi
  echo "Installing coreutils/realpath"
  brew install coreutils >&/dev/null
fi

thisDir=$( dirname "`realpath "$0"`" )
echo "This script is run from \"$thisDir\""


에러는 에러, 에러가 합니다.brew를 인스톨 할 수도 있습니다만, 그 이외의 방법으로 인스톨 할 수도 있습니다.그냥 인터넷에서 임의의 루비 코드를 감아주는 자동화가 불편했어요.

이것은 올레그 미키예프의 대답에 대한 자동 변형입니다.


하나의 중요한 테스트

이러한 솔루션의 적절한 테스트의 1개는 다음과 같습니다.

  1. 어딘가에 코드를 스크립트 파일에 넣다
  2. 심볼링크')ln -s )을합니다.
  3. 해당 심볼링크에서 스크립트 실행

솔루션은 심볼 링크를 참조 해제하고 원본 디렉터리를 제공합니까?그렇다면 효과가 있습니다.

이것은 OSX에서 동작하는 것처럼 보이며 바이너리가 필요 없으며 여기서 가져온 것입니다.

function normpath() {
  # Remove all /./ sequences.
  local path=${1//\/.\//\/}

  # Remove dir/.. sequences.
  while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
    path=${path/${BASH_REMATCH[0]}/}
  done
  echo $path
}

마음에 들어요.

#!/usr/bin/env bash
function realpath() {
    local _X="$PWD"
    local _LNK=$1
    cd "$(dirname "$_LNK")"
    if [ -h "$_LNK" ]; then
        _LNK="$(readlink "$_LNK")"
        cd "$(dirname "$_LNK")"
    fi
    echo "$PWD/$(basename "$_LNK")"
    cd "$_X"
}

는 난필이 필요했다.realpathOS X에서의 치환(Symblinks 및 상위 참조가 있는 경로에서 정상적으로 동작하는 OS X에서의 치환).여기에는 예를 들어 홈브루를 설치한 경우 등 상위 참조를 해결하기 전에 경로의 심볼 링크를 해결하는 작업이 포함됩니다.coreutils병, 실행:

$ ln -s /var/log/cups /tmp/linkeddir  # symlink to another directory
$ greadlink -f /tmp/linkeddir/..      # canonical path of the link parent
/private/var/log

주의:readlink -f해결했습니다/tmp/linkeddir 문제를 해결하기 전에..parent dir 참조.물론 없습니다.readlink -fMac에서도 마찬가지입니다.

따라서 bash 구현의 일부로서realpathGNUlib 함수 호출이 하는 것을 Bash 3.2에서 다시 구현했습니다.이것은 GNU가 하는 함수 호출이기도 합니다.readlink -f 라이선스:

# shellcheck shell=bash
set -euo pipefail

_contains() {
    # return true if first argument is present in the other arguments
    local elem value

    value="$1"
    shift

    for elem in "$@"; do 
        if [[ $elem == "$value" ]]; then
            return 0
        fi
    done
    return 1
}

_canonicalize_filename_mode() {
    # resolve any symlink targets, GNU readlink -f style
    # where every path component except the last should exist and is
    # resolved if it is a symlink. This is essentially a re-implementation
    # of canonicalize_filename_mode(path, CAN_ALL_BUT_LAST).
    # takes the path to canonicalize as first argument

    local path result component seen
    seen=()
    path="$1"
    result="/"
    if [[ $path != /* ]]; then  # add in current working dir if relative
        result="$PWD"
    fi
    while [[ -n $path ]]; do
        component="${path%%/*}"
        case "$component" in
            '') # empty because it started with /
                path="${path:1}" ;;
            .)  # ./ current directory, do nothing
                path="${path:1}" ;;
            ..) # ../ parent directory
                if [[ $result != "/" ]]; then  # not at the root?
                    result="${result%/*}"      # then remove one element from the path
                fi
                path="${path:2}" ;;
            *)
                # add this component to the result, remove from path
                if [[ $result != */ ]]; then
                    result="$result/"
                fi
                result="$result$component"
                path="${path:${#component}}"
                # element must exist, unless this is the final component
                if [[ $path =~ [^/] && ! -e $result ]]; then
                    echo "$1: No such file or directory" >&2
                    return 1
                fi
                # if the result is a link, prefix it to the path, to continue resolving
                if [[ -L $result ]]; then
                    if _contains "$result" "${seen[@]+"${seen[@]}"}"; then
                        # we've seen this link before, abort
                        echo "$1: Too many levels of symbolic links" >&2
                        return 1
                    fi
                    seen+=("$result")
                    path="$(readlink "$result")$path"
                    if [[ $path = /* ]]; then
                        # if the link is absolute, restart the result from /
                        result="/"
                    elif [[ $result != "/" ]]; then
                        # otherwise remove the basename of the link from the result
                        result="${result%/*}"
                    fi
                elif [[ $path =~ [^/] && ! -d $result ]]; then
                    # otherwise all but the last element must be a dir
                    echo "$1: Not a directory" >&2
                    return 1
                fi
                ;;
        esac
    done
    echo "$result"
}

여기에는 순환 심볼링크 검출, 같은 (중간) 경로가 두 번 나타나면 종료가 포함됩니다.

한 것은 ★★★★★★★★★★★★★★★★★★★」readlink -f그럼 위의 내용을 다음과 같이 사용할 수 있습니다.

readlink() {
    if [[ $1 != -f ]]; then  # poor-man's option parsing
        # delegate to the standard readlink command
        command readlink "$@"
        return
    fi

    local path result seenerr
    shift
    seenerr=
    for path in "$@"; do
        # by default readlink suppresses error messages
        if ! result=$(_canonicalize_filename_mode "$path" 2>/dev/null); then
            seenerr=1
            continue
        fi
        echo "$result"
    done
    if [[ $seenerr ]]; then
        return 1;
    fi
}

★★★의 realpath 도 needed needed needed , , ,가 했다.--relative-to ★★★★★★★★★★★★★★★★★」--relative-base: 후 경로를 제공합니다.

_realpath() {
    # GNU realpath replacement for bash 3.2 (OS X)
    # accepts --relative-to= and --relative-base options
    # and produces canonical (relative or absolute) paths for each
    # argument on stdout, errors on stderr, and returns 0 on success
    # and 1 if at least 1 path triggered an error.

    local relative_to relative_base seenerr path

    relative_to=
    relative_base=
    seenerr=

    while [[ $# -gt 0 ]]; do
        case $1 in
            "--relative-to="*)
                relative_to=$(_canonicalize_filename_mode "${1#*=}")
                shift 1;;
            "--relative-base="*)
                relative_base=$(_canonicalize_filename_mode "${1#*=}")
                shift 1;;
            *)
                break;;
        esac
    done

    if [[
        -n $relative_to
        && -n $relative_base
        && ${relative_to#${relative_base}/} == "$relative_to"
    ]]; then
        # relative_to is not a subdir of relative_base -> ignore both
        relative_to=
        relative_base=
    elif [[ -z $relative_to && -n $relative_base ]]; then
        # if relative_to has not been set but relative_base has, then
        # set relative_to from relative_base, simplifies logic later on
        relative_to="$relative_base"
    fi

    for path in "$@"; do
        if ! real=$(_canonicalize_filename_mode "$path"); then
            seenerr=1
            continue
        fi

        # make path relative if so required
        if [[
            -n $relative_to
            && ( # path must not be outside relative_base to be made relative
                -z $relative_base || ${real#${relative_base}/} != "$real"
            )
        ]]; then
            local common_part parentrefs

            common_part="$relative_to"
            parentrefs=
            while [[ ${real#${common_part}/} == "$real" ]]; do
                common_part="$(dirname "$common_part")"
                parentrefs="..${parentrefs:+/$parentrefs}"
            done

            if [[ $common_part != "/" ]]; then
                real="${parentrefs:+${parentrefs}/}${real#${common_part}/}"
            fi
        fi

        echo "$real"
    done
    if [[ $seenerr ]]; then
        return 1
    fi
}

if ! command -v realpath > /dev/null 2>&1; then
    # realpath is not available on OSX unless you install the `coreutils` brew
    realpath() { _realpath "$@"; }
fi

코드에 대한 코드 리뷰 요청에 유닛 테스트를 포함시켰습니다.

bash를 사용하는 Mac에서 nodejs 개발자의 경우:

realpath() {
  node -p "fs.realpathSync('$1')"
}

★★★★★★★★★★의 사용법pushd:

realpath() {
  eval echo "$(pushd $(dirname "$1") | cut -d' ' -f1)/$(basename "$1")"
}

eval다음과 같은 칠데를 확장하기 위해 사용됩니다.~/Downloads.

코멘터와의 커뮤니케이션에 근거해, Ubuntu와 완전히 같은 리얼 패스를 실장하는 3가지 방법이 없고, 매우 어렵다고 하는 것에 동의했습니다.

하지만 다음 버전에서는 가장 좋은 답변은 할 수 없고 맥북에 대한 나의 일상의 요구를 충족시킬 수 있습니다.이 코드를 ~/.bashrc에 입력하고 다음 사항을 기억하십시오.

  • arg는 1개의 파일 또는 dir만 사용할 수 있으며 와일드카드 없음
  • dir 또는 파일 이름에 공백 없음
  • 적어도 파일 또는 dir의 부모 dir가 존재합니다.
  • 얼마든지 사용하세요. / 이것들, 이것들은 안전합니다.

    # 1. if is a dir, try cd and pwd
    # 2. if is a file, try cd its parent and concat dir+file
    realpath() {
     [ "$1" = "" ] && return 1

     dir=`dirname "$1"`
     file=`basename "$1"`

     last=`pwd`

     [ -d "$dir" ] && cd $dir || return 1
     if [ -d "$file" ];
     then
       # case 1
       cd $file && pwd || return 1
     else
       # case 2
       echo `pwd`/$file | sed 's/\/\//\//g'
     fi

     cd $last
    }

언급URL : https://stackoverflow.com/questions/3572030/bash-script-absolute-path-with-os-x

반응형