Абсолютный путь скрипта Bash с OS X - PullRequest
76 голосов
/ 26 августа 2010

Я пытаюсь получить абсолютный путь к текущему запущенному сценарию на OS X.

Я видел много ответов, идущих за readlink -f $0.Однако, поскольку readlink в OS X совпадает с BSD, он просто не работает (работает с версией GNU).

Существует ли готовое решение для этого?

Ответы [ 11 ]

90 голосов
/ 15 мая 2015

Эти три простых шага помогут решить эту и многие другие проблемы OS X:

  1. Установка Homebrew
  2. brew install coreutils
  3. grealpath .

(3) может быть изменено на realpath, см. (2) вывод

70 голосов
/ 26 августа 2010

Есть функция realpath() C, которая выполнит эту работу, но я не вижу ничего доступного в командной строке.Вот быстрая и грязная замена:

#!/bin/bash

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

realpath "$0"

Печатается дословно, если путь начинается с /.Если нет, то это должен быть относительный путь, поэтому он стоит $PWD впереди.Часть #./ снимает ./ с передней стороны $1.

21 голосов
/ 26 августа 2013

Тьфу.Мне показалось, что предыдущие ответы немного интересны по нескольким причинам: в частности, они не разрешают несколько уровней символьных ссылок, и они чрезвычайно "Bash-y".Хотя первоначальный вопрос явно запрашивает «сценарий Bash», в нем также упоминается BSD-подобный Mac OS X не GNU readlink.Итак, вот попытка разумной переносимости (я проверил это с помощью bash как 'sh' и dash), разрешив произвольное количество символических ссылок;и он также должен работать с пробелами в пути (ах), хотя я не уверен в поведении, если есть пробел, базовое имя самой утилиты, так что, может быть, избежать этого?

#!/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 "$@"

Надеюсь, что это кому-то пригодится.

10 голосов
/ 31 июля 2017

Более дружественный к командной строке вариант решения Python:

python -c "import os; print(os.path.realpath('$1'))"
7 голосов
/ 28 мая 2017

Используйте Python, чтобы получить его:

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

print(os.path.realpath(sys.argv[1]))
6 голосов
/ 16 октября 2017

Поскольку существует realpath , как указали другие:

// 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:

#Makefile
OBJ = realpath.o

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

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

Затем скомпилируйте с make и вставьте мягкую ссылку с:
ln -s $(pwd)/realpath /usr/local/bin/realpath

6 голосов
/ 12 июня 2015

Я искал решение для использования в сценарии предоставления системы, т. Е. Запустить до того, как Homebrew будет даже установлен.Не имея правильного решения, я просто перенесу задачу на кроссплатформенный язык, например, Perl:

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

Чаще всего нам действительно нужен каталог, содержащий:

2 голосов
/ 21 декабря 2013

Итак, как вы можете видеть выше, я сделал снимок около 6 месяцев назад. Я полностью забыл об этом, пока не обнаружил, что снова нуждаюсь в подобной вещи. я был полностью шокирован , чтобы увидеть, насколько это было элементарно; Я учил Я довольно интенсивно кодирую уже около года, но я часто чувствую, что может быть, я совсем ничего не узнал, когда дела идут хуже всего.

Я бы убрал вышеприведенное «решение», но мне действительно нравится, когда я записываю о том, сколько я действительно узнал за последние несколько месяцев.

Но я отвлекся. Я сел и решил все это прошлой ночью. Объяснение в комментариев должно быть достаточно. Если вы хотите отслеживать копию, я продолжаю для работы, вы можете следовать этой сути. Это, вероятно, делает то, что вам нужно.

#!/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
1 голос
/ 21 января 2018

realpath для Mac OS X

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

Пример с соответствующим путем:

realpath "../scripts/test.sh"

Пример с домашней папкой

realpath "~/Test/../Test/scripts/test.sh"
0 голосов
/ 13 июня 2019

В macOS единственным решением, которое я нашел для этого, который надежно обрабатывает символические ссылки, является использование 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. поместите код в файл сценария где-то
  2. в другой каталог, символическая ссылка (ln -s) к этому файлу
  3. запустите скрипт из этой символической ссылки

Разрешает ли решение разыменовать символическую ссылку и дать вам исходный каталог?Если так, это работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...