Linuxでは、readlink
ユーティリティは、追加のリンクに続くオプション-f
を受け入れます。これはMacおよびおそらくBSDベースのシステムでは動作しないようです。同等のものは何でしょうか?
デバッグ情報は次のとおりです。
$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
readlink -f
は2つのことを行います:
必要に応じて、Vanillaの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 readlink。
MacPortsとHomebrewは、greadlink
(GNU readlink)を含むcoreutilsパッケージを提供します。 mackb.comのMichael Kallweitt投稿の功績。
brew install coreutils
greadlink -f file.txt
realpath(3)
、またはPythonの os.path.realpath
に興味があるかもしれません。 2つはまったく同じではありません。 Cライブラリの呼び出しでは、中間パスコンポーネントが存在する必要がありますが、Pythonバージョンは存在しません。
$ 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とctypes(Mac OS X 10.5で利用可能)を使用してライブラリをラップできますコール:
#!/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 __== '__main__':
print realpath(sys.argv[1])
皮肉なことに、このスクリプトのCバージョンは短くする必要があります。 :)
私はさらに別の実装を積み重ねることを嫌いますが、a)ポータブルで純粋なシェル実装、およびb)単体テストカバレッジ、このようなもののエッジケースの数はnon-trivialです。
テストと完全なコードについては、 Githubでの私のプロジェクト を参照してください。実装の概要は次のとおりです。
キース・スミスが率直に指摘するように、readlink -f
は2つのことを行います。1)シンボリックリンクを再帰的に解決し、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
}
これは 完全な実装 のわずかに簡略化されたバージョンであることに注意してください。 完全な実装では、シンボリックリンクサイクルの小さなチェックが追加され、出力が少しマッサージされます。
最後に、パスを正規化する関数:
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")
}
多かれ少なかれそれだけです。スクリプトに貼り付けるのに十分単純ですが、ユースケースの単体テストを持たないコードに頼るのは夢中になるほどトリッキーです。
greadlinkは、-fを実装するgnu readlinkです。 macportsなどを使用することもできます。私はhomebrewを好みます。
Perlのシンプルなワンライナーは、外部の依存関係なしでほぼどこでも動作します。
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 には、NetBSDから派生したstat
のバージョンがあります。
フォーマットスイッチを使用して出力を調整できます(上記のリンクのマニュアルページを参照)。
% 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
stat
の一部のOS Xバージョンには、フォーマット用の-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
働くでしょう。
以下は、ANYBourneに匹敵するシェルで動作する移植可能なシェル関数です。相対パスの句読点「..または。」を解決します。シンボリックリンクを逆参照します。
何らかの理由で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%純粋なシェルコードでbasenameと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##*/}"
}
このシェルコードの更新バージョンは、Googleサイトで見つけることができます: http://sites.google.com/site/jdisnard/realpath
編集:このコードは、2節(freeBSDスタイル)ライセンスの条件の下でライセンスされています。ライセンスのコピーは、私のサイトへの上記のハイパーリンクをたどって見つけることができます。
この問題を解決し、HomebrewまたはFreeBSDがインストールされているMacでreadlinkの機能を有効にする最も簡単な方法は、「coreutils」パッケージをインストールすることです。特定のLinuxディストリビューションおよび他のPOSIX OSでも必要になる場合があります。
たとえば、FreeBSD 11では、次を呼び出してインストールしました。
# pkg install coreutils
Homebrewを使用したMacOSでは、コマンドは次のようになります。
$ brew install coreutils
なぜ他の答えがそんなに複雑なのかはよくわかりませんが、それだけです。ファイルは別の場所にはなく、まだインストールされていないだけです。
更新の開始
これは頻繁に発生する問題であるため、realpath-libと呼ばれるBash 4ライブラリを無料で使用するために作成しました(MITライセンス)。これは、デフォルトでreadlink -fをエミュレートするように設計されており、特定のUNIXシステムで動作することを確認する2つのテストスイートと(2) readlink -fがインストールされている場合(ただし、これは必須ではありません)。さらに、深い、壊れたシンボリックリンクおよび循環参照を調査、識別、展開するために使用できるため、深くネストされた物理またはシンボリックディレクトリおよびファイルの問題を診断するための便利なツールになります。 github.com または bitbucket.org にあります。
更新の終了
Bash以外に依存しない、非常にコンパクトで効率的な別のソリューションは次のとおりです。
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"
}
本当にプラットフォームに依存しないのもこのR-onlinerです
readlink(){ RScript -e "cat(normalizePath(commandArgs(T)[1]))" "$1";}
readlink -f <path>
を実際に模倣するには、$ 1ではなく$ 2を使用する必要があります。
私の仕事は非BSD LinuxとmacOSの両方の人々によって使用されているため、ビルドスクリプトでこれらのエイリアスを使用することを選択しました(同様の問題があるため、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
}
自動的にインストールするために追加できるオプションのチェック homebrew + 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マークに近いでしょう。
決して遅くないほうがいいと思う。 FedoraスクリプトがMacで動作していなかったので、私は特にこれを開発する意欲がありました。問題は依存関係とBashです。 Macにはそれらがありません。もしあれば、他の場所(別のパス)にあることがよくあります。クロスプラットフォームのBashスクリプトでの依存関係のパス操作は、せいぜい頭痛の種であり、最悪の場合はセキュリティリスクです。そのため、可能であれば、その使用を避けるのが最善です。
以下のget_realpath()関数はシンプルでBash中心であり、依存関係は必要ありません。 Bashビルトインechoとcdのみを使用します。方法の各段階ですべてがテストされ、エラー条件が返されるため、かなり安全です。
シンボリックリンクを追跡したくない場合は、スクリプトの先頭にset -Pを配置しますが、それ以外の場合はcdは、デフォルトでシンボリックリンクを解決する必要があります。 {絶対|相対|シンボリックリンク| local}そして、ファイルへの絶対パスを返します。これまでのところ、問題はありませんでした。
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と組み合わせることができます。これらは、GitHubリポジトリで realpath-lib として見つけることができます(完全な開示-これは当社の製品ですが、制限なしでコミュニティに無料で提供しています)。また、教育ツールとしての役割も果たします-十分に文書化されています。
いわゆる「モダンバッシュ」の実践に最善を尽くしていますが、バッシュは大きな課題であり、常に改善の余地があると確信しています。 Bash 4+が必要ですが、まだ残っている場合は古いバージョンで動作するようにすることができます。
OS X用のrealpathユーティリティ と書いたので、readlink -f
と同じ結果が得られます。
以下に例を示します。
(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
パッケージで、GNU/Linuxコアユーティリティをインストールします。これらのユーティリティは、Mac OSX実装に対応しているため、これらを使用できます。
Mac osxシステムには、Linux coreutils(「Core Utilities」)に似ているように見えるプログラムやユーティリティがありますが、いくつかの点で異なっています(フラグが異なるなど)。
これは、これらのツールのMac OSX実装が異なるためです。元のGNU/Linuxのような動作を得るには、coreutils
パッケージ管理システムを介してbrew
パッケージをインストールできます。
これにより、対応するコアユーティリティがインストールされ、接頭辞g
が付けられます。例えば。 readlink
には、対応するgreadlink
プログラムがあります。
readlink
をGNU readlink
(greadlink
)実装のように実行するには、coreutilsのインストール後に簡単なエイリアスを作成できます。
実装
https://brew.sh/ の指示に従ってください
brew install coreutils
エイリアスは〜/ .bashrc、〜/ .bash_profile、またはbashエイリアスの保持に慣れている場所に配置できます。私は個人的に〜/ .bashrcに保存します
alias readlink=greadlink
Gmv、gdu、gdfなどの他のcoreutilsに対して同様のエイリアスを作成できます。しかし、MacマシンのGNU動作は、ネイティブcoreutilsを使用する他のユーザーを混乱させたり、Macシステムで予期しない動作をする可能性があることに注意してください。