Абсолютный путь скрипта Bash с OS X
Я пытаюсь получить абсолютный путь к текущему запущенному сценарию на OS X.
Я видел много ответов на readlink -f $0
. Однако, поскольку readlink
OS X такая же, как у BSD, она просто не работает (она работает с версией GNU).
Есть ли готовое решение для этого?
Ответы
Ответ 1
Там есть функция realpath()
C, которая выполнит задание, но я не вижу ничего доступного в командной строке. Вот быстрая и грязная замена:
#!/bin/bash
realpath() {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
realpath "$0"
Это печатает путь дословно, если он начинается с /
. Если нет, это должен быть относительный путь, поэтому он добавляет $PWD
к фронту. Часть #./
отбрасывает ./
с фронта $1
.
Ответ 2
Эти три простых шага помогут решить эту и многие другие проблемы OS X:
- Установить Homebrew
-
brew install coreutils
-
grealpath.
(3) может быть изменен на realpath
, см. (2) вывод
Ответ 3
Тьфу. Я нашел ответы на предыдущие ответы немного по нескольким причинам: в частности, они не разрешают множественные уровни символических ссылок, и они чрезвычайно "Bash -y". Хотя исходный вопрос явно запрашивает "Bash script", он также упоминает Mac OS X BSD-like, non-GNU readlink
. Итак, вот попытка некоторой разумной переносимости (я проверил ее с помощью bash как "sh" и тире), разрешив произвольное количество символических ссылок; и он также должен работать с пробелами в пути (-ах), хотя я не уверен в поведении, если есть пустое пространство базового имени самой утилиты, так что, возможно, um, избегайте этого?
#!/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 "[email protected]"
Надеюсь, что это может кому-то помочь.
Ответ 4
Более удобный для командной строки вариант решения Python:
python -c "import os; print(os.path.realpath('$1'))"
Ответ 5
Я искал решение для использования в системном обеспечении script, т.е. запускается до того, как 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")
Ответ 6
Используйте Python, чтобы получить его:
#!/usr/bin/env python
import os
import sys
print(os.path.realpath(sys.argv[1]))
Ответ 7
Так как существует реальный путь, как указали другие:
// 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
Ответ 8
Итак, как вы можете видеть выше, я сделал снимок примерно 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 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 "$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 "
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
Ответ 9
реальный путь для 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"
Ответ 10
Как об этом в качестве альтернативы?
(Игнорировать изменения формата) для всех файлов с путями (относительными или абсолютными) - cd в директорию с файлом и использовать там каталог.
Это очищает любой относительный (т.е. /tmp/../etc/passwd
= > будет отображаться как /etc/passwd
)
[не считая этого, видел его где-то много лет назад]
function abspath()
{
case "${1}" in
[./]*)
echo "$(cd ${1%/*}; pwd)/${1##*/}"
;;
*)
echo "${PWD}/${1}"
;;
esac
}
Ответ 11
В macOS единственное решение, которое я нашел для этого, который надежно обрабатывает символические realpath
- это использование 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
, но вы также можете просто установить его. Я просто не чувствовал себя комфортно, автоматизируя что-то, что скручивает произвольный код ruby из сети.
Обратите внимание, что это автоматический вариант ответа Олега Михеева.
Один важный тест
Один хороший тест любого из этих решений:
- поместите код в файл сценария
- в другом каталоге, символическая ссылка (
ln -s
) на этот файл - запустить скрипт из этой символической ссылки
Решение разыменовывает символическую ссылку и дает вам оригинальный каталог? Если так, это работает.
Ответ 12
Здесь фрагмент C, который разрешает символические ссылки, а также псевдонимы Mac-типа:
http://my-sample-code.googlecode.com/svn/trunk/realpath/realpath.c
Ответ 13
Взгляните на этот вопрос. Я нашел ответ более кратким.
получить путь, в котором оболочка osx script находится внутри script... когда путь имеет пробел
Ответ 14
Кажется, это работает для OSX, не требует никаких двоичных файлов, и было извлечено отсюда
function normpath() {
# Remove all /./ sequences.
local path=${1//\/.\//\/}
# Remove dir/.. sequences.
while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
path=${path/${BASH_REMATCH[0]}/}
done
echo $path
}
Ответ 15
Основываясь на общении с комментатором, я согласился, что это очень сложно и не имеет тривиального способа реализовать realpath, который ведет себя так же, как Ubuntu.
Но следующая версия, может обрабатывать угловые случаи, лучший ответ не может и удовлетворить мои ежедневные потребности в MacBook. Поместите этот код в ваш ~/.bashrc и запомните:
- arg может быть только 1 файлом или 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
}