OS Xで現在実行中のスクリプトへの絶対パスを取得しようとしています。
readlink -f $0
。ただし、OS Xのreadlink
はBSDと同じであるため、動作しません(GNUのバージョンで動作します)。
これにすぐに使えるソリューションはありますか?
仕事をするrealpath()
C関数がありますが、コマンドラインで利用できるものは何もありません。迅速で汚れた代替品を次に示します。
#!/bin/bash
realpath() {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
realpath "$0"
これは、/
で始まるパスをそのまま出力します。そうでない場合は、相対パスである必要があるため、$PWD
を先頭に追加します。 #./
部分は、./
の前から$1
を取り除きます。
次の3つの簡単な手順で、この問題や他の多くのOS Xの問題を解決できます。
brew install coreutils
grealpath .
(3)単にrealpath
に変更される場合があります。(2)出力を参照してください
あーいくつかの理由で、以前の回答が少し欲しかったことがわかりました。特に、それらは複数レベルのシンボリックリンクを解決せず、非常に「Bash-y」です。元の質問では「Bashスクリプト」を明示的に要求していますが、Mac OS XのBSDに似た非GNU readlink
についても言及しています。そこで、ここでは、合理的なポータビリティ(「sh」と「dash」としてbashで確認しました)を試み、任意の数のシンボリックリンクを解決します。また、パス内の空白でも動作するはずですが、ユーティリティ自体のベース名に空白がある場合の動作はわかりませんが、おそらく、それを避けてください?
#!/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; print(os.path.realpath('$1'))"
システム提供スクリプトで使用するソリューション、つまり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")
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
OBJ = realpath.o
%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
realpath: $(OBJ)
gcc -o $@ $^ $(CFLAGS)
_
次に、make
でコンパイルし、次のようにソフトリンクを設定します。ln -s $(pwd)/realpath /usr/local/bin/realpath
Pythonを使用して取得してください:
#!/usr/bin/env python
import os
import sys
print(os.path.realpath(sys.argv[1]))
上のように、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
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"
MacOSでは、これに対してsymlinksを確実に処理する唯一の解決策は、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コードをネットからカールする何かを自動化することに不安を感じました。
これは、Oleg Mikheevの answer の自動化されたバリエーションであることに注意してください。
これらのソリューションのいずれかの良いテストは次のとおりです。
ln -s
)そのファイルへソリューションはシンボリックリンクを逆参照し、元のディレクトリを提供しますか?もしそうなら、それは動作します。