Абсолютный путь скрипта Bash с OSX


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

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

любые предложения для решения этого?

10   56  

10 ответов:

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

#!/bin/bash

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

realpath ""

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

эти три простых шага собираются решить эту и многие другие проблемы OSX:

  1. установить доморощенного
  2. brew install coreutils
  3. grealpath .

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

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

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

надеюсь, что это может быть полезно кому-то.

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

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

чаще всего то, что мы на самом деле хотим, это содержащий каталог:

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

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

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


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

так как есть 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 [email protected] $< $(CFLAGS)

realpath: $(OBJ)
      gcc -o [email protected] $^ $(CFLAGS)

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

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

python -c "import os; print(os.path.realpath(''))"

Итак, как вы можете видеть выше, я сделал снимок в этом около 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 ""
## specificies that the  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: "[email protected]"\n" ;} # Borrow a horrible signal name.

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

pathfull(){ cd "$(dirname "[email protected]")"; link="$(readlink "$(basename "[email protected]")")"

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

 if [ ! -e "[email protected]" ]; then if $(ls -d "[email protected]" 2>/dev/null) 2>/dev/null;  then
    errnoent 1>&2; exit 1; elif [ ! -e "[email protected]" -a "$link" = "[email protected]" ];   then
    recurses 1>&2; exit 1; elif [ ! -e "[email protected]" ] && [ ! -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 "[email protected]" | cut -c1)" = '/' ]; then
   printf "[email protected]\n"; exit 0; else printf "$(pwd)/$(basename "[email protected]")\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 "".

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

# Yay ANSI l33t codes! Fancy.
 printf "\n3[3mfrom/as: 3[4m3[0m\n\n3[1mUSAGE:3[0m   "
 printf "3[4m$scriptname3[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         3[4m$scriptname3[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 "[email protected]"
fi

realpath для Mac OS X

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

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

realpath "../scripts/test.sh"

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

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