Абсолютный путь скрипта Bash с OSX
Я пытаюсь получить абсолютный путь к текущему скрипту на OS X.
Я видел много ответов, идущих на readlink -f
. Однако начиная с OS X readlink
это то же самое, что и BSD, он просто не работает (он работает с версией GNU).
любые предложения для решения этого?
10 ответов:
здесь
realpath()
C функция, которая будет выполнять эту работу, но я не вижу ничего доступного в командной строке. Вот быстрая и грязная замена:#!/bin/bash realpath() { [[ = /* ]] && echo "" || echo "$PWD/${1#./}" } realpath ""
это печатает путь дословно, если он начинается с
/
. Если нет, то это должен быть относительный путь, поэтому он добавляет$PWD
на фронт. Элемент#./
часть снимает./
спереди.
эти три простых шага собираются решить эту и многие другие проблемы OSX:
- установить доморощенного
brew install coreutils
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 "$@"
надеюсь, что это может быть полезно кому-то.
Я искал решение для использования в скрипте обеспечения системы, т. е. запуск до того, как 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 $@ $< $(CFLAGS) realpath: $(OBJ) gcc -o $@ $^ $(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: "$@"\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 "". 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 "$@" 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"
взгляните на этот вопрос. Я нашел ответ там более кратким.
получение пути, по которому сценарий оболочки osx находится в сценарии...когда путь имеет пробелы