Mac 上で GNU の readlink -f の動作を取得するにはどうすればよいですか?
質問
Linux では、 readlink
ユーティリティはオプションを受け入れます -f
追加のリンクが続きます。これは Mac とおそらく BSD ベースのシステムでは機能しないようです。同等のものは何でしょうか?
デバッグ情報は次のとおりです。
$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
解決
readlink -f
次の 2 つのことを行います。
- 実際のファイルが見つかるまで、一連のシンボリックリンクに沿って反復処理が行われます。
- そのファイルの 正規化された name - つまり、その絶対パス名。
必要に応じて、バニラの readlink 動作を使用するシェル スクリプトを構築するだけで、同じことを実現できます。ここに例を示します。明らかに、これを独自のスクリプトの呼び出したい場所に挿入できます。 readlink -f
#!/bin/sh
TARGET_FILE=$1
cd `dirname $TARGET_FILE`
TARGET_FILE=`basename $TARGET_FILE`
# Iterate down a (possible) chain of symlinks
while [ -L "$TARGET_FILE" ]
do
TARGET_FILE=`readlink $TARGET_FILE`
cd `dirname $TARGET_FILE`
TARGET_FILE=`basename $TARGET_FILE`
done
# Compute the canonicalized name by finding the physical path
# for the directory we're in and appending the target file.
PHYS_DIR=`pwd -P`
RESULT=$PHYS_DIR/$TARGET_FILE
echo $RESULT
これにはエラー処理が含まれないことに注意してください。特に重要なのは、シンボリックリンク サイクルを検出しないことです。これを行う簡単な方法は、ループを一周する回数を数え、1,000 などのありそうもない大きな数値に達した場合に失敗することです。
使用するために編集されました pwd -P
の代わりに $PWD
.
このスクリプトは次のように呼び出されることを想定していることに注意してください。 ./script_name filename
, 、 いいえ -f
, 、 変化 $1
に $2
と一緒に使用できるようにしたい場合は -f filename
GNU リードリンクのようなものです。
他のヒント
MacPortsのと自作はgreadlink
(GNUのREADLINK)を含有する coreutilsののパッケージを提供します。 mackb.comでマイケルKallweittポストの功績ます。
brew install coreutils
greadlink -f file.txt
あなたは realpath(3)
のに興味がある可能性があり、またはPythonの os.path.realpath
に。 2はまったく同じではありません。 PythonのバージョンはないながらCライブラリ呼び出しは、中間パス成分が存在することを必要とする。
$ pwd
/tmp/foo
$ ls -l
total 16
-rw-r--r-- 1 miles wheel 0 Jul 11 21:08 a
lrwxr-xr-x 1 miles wheel 1 Jul 11 20:49 b -> a
lrwxr-xr-x 1 miles wheel 1 Jul 11 20:49 c -> b
$ python -c 'import os,sys;print(os.path.realpath(sys.argv[1]))' c
/private/tmp/foo/a
私はあなたが他のスクリプト言語よりも軽量なものを好むだろうと知っているが、バイナリをコンパイルする鼻持ちならないで念のために、あなたはライブラリの呼び出しをラップするPythonとする(Mac OS X 10.5上で利用可能)のctypesを使用することができます
#!/usr/bin/python
import ctypes, sys
libc = ctypes.CDLL('libc.dylib')
libc.realpath.restype = ctypes.c_char_p
libc.__error.restype = ctypes.POINTER(ctypes.c_int)
libc.strerror.restype = ctypes.c_char_p
def realpath(path):
buffer = ctypes.create_string_buffer(1024) # PATH_MAX
if libc.realpath(path, buffer):
return buffer.value
else:
errno = libc.__error().contents.value
raise OSError(errno, "%s: %s" % (libc.strerror(errno), buffer.value))
if __name__ == '__main__':
print realpath(sys.argv[1])
皮肉なことに、このスクリプトのCバージョンは短くなるはずです。 :)
数として、私はまだ別の実装とに積み重ねする嫌いが、私は必要に応じて)のポータブル、純粋なシェル実装する、およびb)のユニットテストカバレッジするエッジ例このような何かのためにあるの非自明の
テストと完全なコードのためのGitHub の上私のプロジェクトを参照してください。以下は、インプリメンテーションの概要は次のとおりです。
1)再帰的にシンボリックリンクを解決し、そして2)したがって、結果を正規化する:キース・スミスが鋭く指摘したように、、readlink -f
は2つのことを行い
realpath() {
canonicalize_path "$(resolve_symlinks "$1")"
}
まず、シンボリックリンクリゾルバの実装ます:
resolve_symlinks() {
local dir_context path
path=$(readlink -- "$1")
if [ $? -eq 0 ]; then
dir_context=$(dirname -- "$1")
resolve_symlinks "$(_prepend_path_if_relative "$dir_context" "$path")"
else
printf '%s\n' "$1"
fi
}
_prepend_path_if_relative() {
case "$2" in
/* ) printf '%s\n' "$2" ;;
* ) printf '%s\n' "$1/$2" ;;
esac
}
これは完全な実装若干簡略化バージョンであることに注意してくださいA>。 の完全な実装は、小さなシンボリックリンクサイクルのためのチェック、並びにマッサージ出力ビットを付加する。
最後に、パスをcanonicalizingための機能ます:
canonicalize_path() {
if [ -d "$1" ]; then
_canonicalize_dir_path "$1"
else
_canonicalize_file_path "$1"
fi
}
_canonicalize_dir_path() {
(cd "$1" 2>/dev/null && pwd -P)
}
_canonicalize_file_path() {
local dir file
dir=$(dirname -- "$1")
file=$(basename -- "$1")
(cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
}
それはそれだ、多かれ少なかれ。あなたのユースケースのためのユニットテストを持っていない任意のコードに依存して夢中になるだろうと、あなたのスクリプトに貼り付けるには十分シンプルですが、十分なトリッキーます。
- 自作をインストールする
- 「brew install coreutils」を実行します。
- 「greadlink -f path」を実行します。
greadlink は、-f を実装する GNU readlink です。macports などを使用することもできますが、私は homebrew を好みます。
簡単なワンライナーます:
perl -MCwd -e 'print Cwd::abs_path shift' ~/non-absolute/file
ウィルデリファレンスシンボリックリンクます。
スクリプトでの使用は、このようなことができます:
readlinkf(){ perl -MCwd -e 'print Cwd::abs_path shift' "$1";}
ABSPATH="$(readlinkf ./non-absolute/file)"
私のような少し何かに見えるのrealpath個人というスクリプトを作っます:
#!/usr/bin/env python
import os.sys
print os.path.realpath(sys.argv[1])
これは何でしょうか?
function readlink() {
DIR="${1%/*}"
(cd "$DIR" && echo "$(pwd -P)")
}
FreeBSD そして OSX ~のバージョンを持っています stat
NetBSD から派生したもの。
フォーマット スイッチを使用して出力を調整できます (上記のリンクにあるマニュアル ページを参照してください)。
% cd /service
% ls -tal
drwxr-xr-x 22 root wheel 27 Aug 25 10:41 ..
drwx------ 3 root wheel 8 Jun 30 13:59 .s6-svscan
drwxr-xr-x 3 root wheel 5 Jun 30 13:34 .
lrwxr-xr-x 1 root wheel 30 Dec 13 2013 clockspeed-adjust -> /var/service/clockspeed-adjust
lrwxr-xr-x 1 root wheel 29 Dec 13 2013 clockspeed-speed -> /var/service/clockspeed-speed
% stat -f%R clockspeed-adjust
/var/service/clockspeed-adjust
% stat -f%Y clockspeed-adjust
/var/service/clockspeed-adjust
一部の OS X バージョン stat
が欠けている可能性があります -f%R
フォーマットのオプション。この場合 -stat -f%Y
十分かもしれません。の -f%Y
オプションはシンボリックリンクのターゲットを表示しますが、 -f%R
は、ファイルに対応する絶対パス名を示します。
編集:
Perl を使用できる場合 (Darwin/OS X には最新バージョンがインストールされています) perl
) それから:
perl -MCwd=abs_path -le 'print abs_path readlink(shift);' linkedfile.txt
働くでしょう。
ここでの中で動作するはずポータブルシェル関数ANY のボーン同等のシェルです。 それは「..か。」相対パス句読点を解決しますそして、シンボリックリンクを間接参照ます。
何らかの理由でのrealpath(1)コマンド、またはREADLINKをお持ちでない場合は、(1)これはエイリアスすることができます。
which realpath || alias realpath='real_path'
お楽しみます:
real_path () {
OIFS=$IFS
IFS='/'
for I in $1
do
# Resolve relative path punctuation.
if [ "$I" = "." ] || [ -z "$I" ]
then continue
elif [ "$I" = ".." ]
then FOO="${FOO%%/${FOO##*/}}"
continue
else FOO="${FOO}/${I}"
fi
## Resolve symbolic links
if [ -h "$FOO" ]
then
IFS=$OIFS
set `ls -l "$FOO"`
while shift ;
do
if [ "$1" = "->" ]
then FOO=$2
shift $#
break
fi
done
IFS='/'
fi
done
IFS=$OIFS
echo "$FOO"
}
また、念のために誰もがここに興味を持っている100%純粋なシェルコードでベース名とのdirnameを実装する方法です。
## http://www.opengroup.org/onlinepubs/000095399/functions/dirname.html
# the dir name excludes the least portion behind the last slash.
dir_name () {
echo "${1%/*}"
}
## http://www.opengroup.org/onlinepubs/000095399/functions/basename.html
# the base name excludes the greatest portion in front of the last slash.
base_name () {
echo "${1##*/}"
}
ます。http://サイトあなたは私のGoogleのサイトで、このシェルコードの更新バージョンを見つけることができます。 google.com/site/jdisnard/realpathする
編集: このコードは、2節(FreeBSDのスタイル)ライセンスの条件の下でライセンスされています。 ライセンスのコピーは、自分のサイトに上記のハイパーリンクを以下により求めることができる。
この問題を解決し、自作設置やFreeBSDは「coreutilsの」パッケージをインストールすることです/ wのMac上でREADLINKの機能を有効にする最も簡単な方法。また、特定のLinuxディストリビューションや他のPOSIX OS上で必要になる場合があります。
例えば、FreeBSDの11で、Iは、呼び出すことによって、インストール
# pkg install coreutils
自作とMacOSの上で、コマンドは次のようになります:
$ brew install coreutils
本当にわからない、なぜ他の答えは、それはそれでお終いです、非常に複雑です。ファイルを別の場所で、彼らはまだインストールされていないされていません。
アップデートの開始
これは非常に頻繁に発生する問題であるため、無料で使用できる (MIT ライセンス) という Bash 4 ライブラリをまとめました。 リアルパス-lib. 。これはエミュレートするように設計されています 読み取りリンク -f デフォルトで、(1) 特定の UNIX システムで動作すること、(2) に対して動作することを検証するための 2 つのテスト スイートが含まれています。 読み取りリンク -f インストールされている場合 (ただし必須ではありません)。さらに、深く壊れたシンボリックリンクや循環参照を調査、特定、修復するために使用できるため、深くネストされた物理的またはシンボリックなディレクトリおよびファイルの問題を診断するのに便利なツールとなります。それは次の場所で見つけることができます github.com または bitbucket.org.
更新の終了
Bash 以外には依存しない、非常にコンパクトで効率的なもう 1 つのソリューションは次のとおりです。
function get_realpath() {
[[ ! -f "$1" ]] && return 1 # failure : file does not exist.
[[ -n "$no_symlinks" ]] && local pwdp='pwd -P' || local pwdp='pwd' # do symlinks.
echo "$( cd "$( echo "${1%/*}" )" 2>/dev/null; $pwdp )"/"${1##*/}" # echo result.
return 0 # success
}
これには環境設定も含まれます no_symlinks
これは、物理システムへのシンボリックリンクを解決する機能を提供します。に限って no_symlinks
何かに設定されています。つまり、 no_symlinks='on'
その後、シンボリックリンクは物理システムに解決されます。それ以外の場合は、それらが適用されます (デフォルト設定)。
これは Bash を提供するすべてのシステムで動作し、テスト目的で Bash 互換の終了コードを返します。
そこの答えの多くはすでにありますが、どれも私のために働いていない...だから、これは私が今使っているものです。
readlink_f() {
local target="$1"
[ -f "$target" ] || return 1 #no nofile
while [ -L "$target" ]; do
target="$(readlink "$target")"
done
echo "$(cd "$(dirname "$target")"; pwd -P)/$target"
}
ないより遅く、私は考えます。私のFedoraのスクリプトは、Mac上で作業していなかったので、私はこれを具体的に開発する意欲的でした。問題は、依存関係とバッシュです。 Macはそれらを持っていない、または彼らが行う場合、彼らは多くの場合、どこか他の場所(別のパス)です。クロスプラットフォームのbashスクリプトで依存パス操作は、最高の状態で頭痛と、最悪の場合、セキュリティ上のリスクである - 。それは、可能な場合には、その使用を避けるのが最善です。
以下関数get_realpath()は、単純な、バッシュ中心であり、依存関係は必要ありません。私はbashの組み込みコマンドエコーの]と[ CD のを使用しています。すべてが道の各段階でテストされますと、それはエラー状態を返すように、それは、また、かなり安全です。
あなたはシンボリックリンクに従うことをしたくない場合は、、そして置くの設定スクリプトの先頭にの-Pが、それ以外の CD のデフォルトでシンボリックリンクを解決する必要があります。これは、{絶対あるファイル引数でテストされています|相対|シンボリックリンク|ローカル}それはファイルの絶対パスを返します。これまでのところ、我々はそれで何か問題があっていませんでした。
function get_realpath() {
if [[ -f "$1" ]]
then
# file *must* exist
if cd "$(echo "${1%/*}")" &>/dev/null
then
# file *may* not be local
# exception is ./file.ext
# try 'cd .; cd -;' *works!*
local tmppwd="$PWD"
cd - &>/dev/null
else
# file *must* be local
local tmppwd="$PWD"
fi
else
# file *cannot* exist
return 1 # failure
fi
# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success
}
あなたは他の関数のget_dirname、get_filename、get_stemnameとvalidate_pathでこれを組み合わせることができます。これは私たちの製品である - これらは、のrealpath-libのの(完全な開示として、当社のGitHubのリポジトリで見つけることができますしかし、我々は)任意の制限なしコミュニティに無料でそれを提供します。また、教育ツールとして役立つことができる - それは十分に文書化されています。
。私たちは、いわゆる「現代バッシュ」プラクティスを適用するために最善を試してみたが、bashは大きな課題であると私は常に改善の余地があるだろう確信しています。それはバッシュ4+が必要ですが、彼らはまだ残っている場合は、古いバージョンで動作させることができます。
本当にプラットフォームindpendentもなり、このR-onliner
readlink(){ RScript -e "cat(normalizePath(commandArgs(T)[1]))" "$1";}
実際には代わりに$ 1の模倣readlink -f <path>
、$ 2を使用する必要があるであろうこと。
、私は(それが同様の問題を持っているので、sed
が含まれている)私たちのビルドスクリプトでこれらのエイリアスを使用するために選んだきます:
##
# If you're running macOS, use homebrew to install greadlink/gsed first:
# brew install coreutils
#
# Example use:
# # Gets the directory of the currently running script
# dotfilesDir=$(dirname "$(globalReadlink -fm "$0")")
# alias al='pico ${dotfilesDir}/aliases.local'
##
function globalReadlink () {
# Use greadlink if on macOS; otherwise use normal readlink
if [[ $OSTYPE == darwin* ]]; then
greadlink "$@"
else
readlink "$@"
fi
}
function globalSed () {
# Use gsed if on macOS; otherwise use normal sed
if [[ $OSTYPE == darwin* ]]; then
gsed "$@"
else
sed "$@"
fi
}
オプションをチェックあなたが自動的にインストールするには、追加することができます自作する + <のhref = "HTTPS://formulae.brew .SH /式/ coreutilsの」REL = "nofollowをnoreferrer"> coreutilsのの依存性:
if [[ "$OSTYPE" == "darwin"* ]]; then
# Install brew if needed
if [ -z "$(which brew)" ]; then
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)";
fi
# Check for coreutils
if [ -z "$(brew ls coreutils)" ]; then
brew install coreutils
fi
fi
私はそれが他の人をチェックする必要がある...しかし、それはおそらく80/20マークに近づいた真の「グローバル」であることを仮定します。
私はreadlink -f
と同じ結果を提供することができますOS X の Aのrealpathユーティリティを書きました。
ここでは一例です:
(jalcazar@mac tmp)$ ls -l a
lrwxrwxrwx 1 jalcazar jalcazar 11 8月 25 19:29 a -> /etc/passwd
(jalcazar@mac tmp)$ realpath a
/etc/passwd
あなたはMacPortsのを使用している場合は、以下のコマンドでそれをインストールすることができます。sudo port selfupdate && sudo port install realpath
説明
coreutils は brew
Mac OSX の実装に対応する GNU/Linux コア ユーティリティをインストールして、それらを使用できるようにするパッケージ
Mac osx システム上で、Linux coreutils (「コア ユーティリティ」) に似ているように見えても、いくつかの点 (フラグが異なるなど) が異なるプログラムやユーティリティを見つけることがあります。
これは、これらのツールの Mac OSX 実装が異なるためです。元の GNU/Linux のような動作を取得するには、 coreutils
経由でパッケージ化する brew
パッケージ管理システム。
これにより、接頭辞が次の対応するコア ユーティリティがインストールされます。 g
. 。例えば。のために readlink
, 、対応するものが見つかります greadlink
プログラム。
作るために readlink
GNU のように実行する readlink
(greadlink
) の実装では、coreutils をインストールした後に簡単なエイリアスを作成できます。
実装
- 醸造をインストールする
次の手順に従ってください。 https://brew.sh/
- coreutils パッケージをインストールする
brew install coreutils
- エイリアスの作成
エイリアスは、~/.bashrc、~/.bash_profile、または bash エイリアスを保持することに慣れている場所に配置できます。私は個人的に ~/.bashrc に保存しています
alias readlink=greadlink
gmv、gdu、gdf などの他の coreutils に対しても同様のエイリアスを作成できます。ただし、Mac マシン上の GNU の動作は、ネイティブ coreutils の操作に慣れている他のユーザーにとって混乱を招く可能性があるか、Mac システム上で予期しない動作をする可能性があることに注意してください。
これは私が使用するものです。
stat -f %N $your_path
READLINKするパスは私のシステムとあなたの間で異なっています。フルパスを指定してみてください。
/sw/sbin/readlink -f
PerlはREADLINK機能を持っている(例えばどのようにしてシンボリックリンクをコピーしますPerlでですか)。これは、OS Xを含むほとんどのプラットフォームで機能します:
perl -e "print readlink '/path/to/link'"
例
$ mkdir -p a/b/c
$ ln -s a/b/c x
$ perl -e "print readlink 'x'"
a/b/c
@Keithスミスからの答えは無限ループを与えます。
ここで私はSunOSの上でのみ使用し、私の答えは、(SunOSはそんなにPOSIXとGNUのコマンドを欠場)です。
それはあなたの$ PATHディレクトリの1つに配置する必要がありますスクリプトファイルです
#!/bin/sh
! (($#)) && echo -e "ERROR: readlink <link to analyze>" 1>&2 && exit 99
link="$1"
while [ -L "$link" ]; do
lastLink="$link"
link=$(/bin/ls -ldq "$link")
link="${link##* -> }"
link=$(realpath "$link")
[ "$link" == "$lastlink" ] && echo -e "ERROR: link loop detected on $link" 1>&2 && break
done
echo "$link"