Linuxでは、ユーティリティは追加のリンクをたどるreadlink
オプションを受け入れます。-f
これは、MacおよびおそらくBSDベースのシステムでは機能しないようです。同等のものは何でしょうか?
デバッグ情報は次のとおりです。
$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
MacPortsとHomebrewは、(GNU readlink)を含むcoreutilsパッケージを提供します。greadlink
mackb.comのMichaelKallweitt投稿のクレジット。
brew install coreutils
greadlink -f file.txt
readlink -f
2つのことを行います:
必要に応じて、バニラ読み取りリンクの動作を使用して同じことを実現するシェルスクリプトを作成できます。これが例です。明らかに、これを呼び出したい独自のスクリプトに挿入できます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
。
このスクリプトは、GNU readlinkのように使用できるようにする場合は、のように呼び出されることを想定していることに注意してください./script_name filename
。-f
$1
$2
-f filename
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 __name__ == '__main__':
print realpath(sys.argv[1])
皮肉なことに、このスクリプトのCバージョンは短くする必要があります。:)
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)"
私はさらに別の実装を積み重ねるのは嫌いですが、このようなもののエッジケースの数は重要であるため、a)ポータブルで純粋なシェル実装、およびb)ユニットテストカバレッジが必要でした。
テストと完全なコードについては、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を実装するgnureadlinkです。Macportsなども使用できます。自作が好きです。
私はrealpathと呼ばれるスクリプトを個人的に作成しました。これは、次のようになります。
#!/usr/bin/env python
import os, sys
print(os.path.realpath(sys.argv[1]))
これはどうですか?
function readlink() {
DIR="${1%/*}"
(cd "$DIR" && echo "$(pwd -P)")
}
私のために働く怠惰な方法、
$ brew install coreutils
$ ln -s /usr/local/bin/greadlink /usr/local/bin/readlink
$ which readlink
/usr/local/bin/readlink
/usr/bin/readlink
実装
https://brew.sh/の指示に従ってください
brew install coreutils
3a。エイリアスを作成する(ユーザーごと)
エイリアスは、〜/ .bashrc、〜/ .bash_profile、またはbashエイリアスの保持に慣れている場所に配置できます。私は個人的に〜/.bashrcに私のものを置いています
alias readlink=greadlink
3b。シンボリックリンクを作成する(システム全体)
ln -s /usr/local/bin/greadlink /usr/local/bin/readlink
(クレジット:イザナ)
これにより、元のreadlinkバイナリをそのまま維持しながら、/ usr / local/binにシンボリックリンクが作成されます。readlinkを検索すると2つの結果が返されるため、これは機能します。ただし、/ usr / local/binの2番目が優先されます。
例えば which readlink
この変更を元に戻すにはunlink /usr/local/bin/readlink
追加ツール
gmv、gdu、gdfなどの他のcoreutilsに対して同様のエイリアスまたはシンボリックリンクを作成できます。ただし、MacマシンでのGNUの動作は、ネイティブのcoreutilsでの作業に慣れている他のユーザーを混乱させたり、Macシステムで予期しない動作をしたりする可能性があることに注意してください。
説明
coreutilsは、brew
それらのMacOSX実装に対応するGNU/ Linuxコアユーティリティをインストールするパッケージであり、それらを使用できます。
Mac osxシステムには、Linux coreutils(「コアユーティリティ」)に似ているように見えるプログラムやユーティリティがありますが、いくつかの点で異なります(フラグが異なるなど)。
これは、これらのツールのMacOSX実装が異なるためです。元のGNU/Linuxのような動作を得るには、パッケージ管理システムcoreutils
を介してパッケージをインストールできます。brew
これにより、接頭辞が。の対応するコアユーティリティがインストールされg
ます。たとえばreadlink
、対応するgreadlink
プログラムがあります。
readlink
GNU readlink
( )実装のように実行するためにgreadlink
、coreutilsをインストールした後に単純なエイリアスまたはシンボリックリンクを作成できます。
FreeBSDとOSXstat
には、 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
一部のOSXバージョンには、フォーマットのオプション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
動作します。
この問題を解決し、HomebrewがインストールされたMacまたはFreeBSDでreadlinkの機能を有効にする最も簡単な方法は、「coreutils」パッケージをインストールすることです。特定のLinuxディストリビューションおよびその他のPOSIXOSでも必要になる場合があります。
たとえば、FreeBSD 11では、次のコマンドを呼び出してインストールしました。
# pkg install coreutils
Homebrewを使用するMacOSでは、コマンドは次のようになります。
$ brew install coreutils
他の答えがなぜそれほど複雑なのかよくわかりません。それだけです。ファイルは別の場所にありません。まだインストールされていないだけです。
これは、 Bourneの同等のシェルで機能するポータブルシェル関数です。相対パスの句読点「..または。」を解決します。シンボリックリンクを逆参照します。
何らかの理由で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##*/}"
}
このシェルコードの更新バージョンは、私のグーグルサイトで見つけることができます:http ://sites.google.com/site/jdisnard/realpath
編集:このコードは、2節(freeBSDスタイル)ライセンスの条件の下でライセンスされています。ライセンスのコピーは、上記の私のサイトへのハイパーリンクをたどることで見つけることができます。
更新を開始
これは非常に頻繁な問題であるため、 realpath-libと呼ばれる無料で使用できるBash 4ライブラリ(MITライセンス)をまとめました。これはデフォルトでreadlink-fをエミュレートするように設計されており、(1)特定のUNIXシステムで動作することを確認するための2つのテストスイートと、(2)インストールされている場合はreadlink -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"
}
私の仕事は非BSDLinuxと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のマークに近づいています。
readlink -f
POSIXシェルスクリプトのPOSIX準拠の実装
https://github.com/ko1nksm/readlinkf
これはPOSIXに準拠しています(bashismはありません)。も使用もしreadlink
ませんrealpath
。GNUと比較して、まったく同じであることを確認しましたreadlink -f
(テスト結果を参照)。エラー処理と優れたパフォーマンスを備えています。から安全に交換できますreadlink -f
。ライセンスはCC0なので、どのプロジェクトでも使用できます。
このコードはbats-coreプロジェクトで採用されています。
# POSIX compliant version
readlinkf_posix() {
[ "${1:-}" ] || return 1
max_symlinks=40
CDPATH='' # to avoid changing to an unexpected directory
target=$1
[ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
[ -d "${target:-/}" ] && target="$target/"
cd -P . 2>/dev/null || return 1
while [ "$max_symlinks" -ge 0 ] && max_symlinks=$((max_symlinks - 1)); do
if [ ! "$target" = "${target%/*}" ]; then
case $target in
/*) cd -P "${target%/*}/" 2>/dev/null || break ;;
*) cd -P "./${target%/*}" 2>/dev/null || break ;;
esac
target=${target##*/}
fi
if [ ! -L "$target" ]; then
target="${PWD%/}${target:+/}${target}"
printf '%s\n' "${target:-/}"
return 0
fi
# `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
# <file mode>, <number of links>, <owner name>, <group name>,
# <size>, <date and time>, <pathname of link>, <contents of link>
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
link=$(ls -dl -- "$target" 2>/dev/null) || break
target=${link#*" $target -> "}
done
return 1
}
最新のコードを参照してください。修正される場合があります。
遅くなるよりはましだと思います。私のFedoraスクリプトがMacで動作していなかったので、私はこれを開発することに動機付けられました。問題は依存関係とBashです。Macにはそれらがないか、ある場合は別の場所(別のパス)にあることがよくあります。クロスプラットフォームのBashスクリプトでの依存関係パスの操作は、せいぜい頭痛の種であり、最悪の場合はセキュリティリスクです。したがって、可能であれば、それらの使用を避けるのが最善です。
以下の関数get_realpath()は単純で、Bash中心であり、依存関係は必要ありません。私はBashビルトインのechoとcdのみを使用しています。また、すべてが途中の各段階でテストされ、エラー状態が返されるため、かなり安全です。
シンボリックリンクをたどりたくない場合は、スクリプトの先頭にset -Pを付けます。それ以外の場合、 cdはデフォルトでシンボリックリンクを解決する必要があります。{absolute|であるファイル引数でテストされています。相対| シンボリックリンク| ローカル}そしてそれはファイルへの絶対パスを返します。これまでのところ、問題は発生していません。
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以降が必要ですが、古いバージョンがまだ存在する場合は、それらで動作するようにすることができます。
echo $(cd $(dirname file1); pwd -P)
と同じ結果を提供できるOSX用の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
。
本当にプラットフォームに依存しないのは、このR-onlinerでもあります
readlink(){ RScript -e "cat(normalizePath(commandArgs(T)[1]))" "$1";}
実際に模倣するにはreadlink -f <path>
、$1ではなく$2を使用する必要があります。
bashスクリプトの先頭に以下を貼り付けただけです。
#!/usr/bin/env bash -e
declare script=$(basename "$0")
declare dirname=$(dirname "$0")
declare scriptDir
if [[ $(uname) == 'Linux' ]];then
# use readlink -f
scriptDir=$(readlink -f "$dirname")
else
# can't use readlink -f, do a pwd -P in the script directory and then switch back
if [[ "$dirname" = '.' ]];then
# don't change directory, we are already inside
scriptDir=$(pwd -P)
else
# switch to the directory and then switch back
pwd=$(pwd)
cd "$dirname"
scriptDir=$(pwd -P)
cd "$pwd"
fi
fi
そして、のすべてのインスタンスを削除しましreadlink -f
た。$scriptDir
その後、$script
スクリプトの残りの部分で使用できるようになります。
これはすべてのシンボリックリンクをたどるわけではありませんが、すべてのシステムで機能し、ほとんどのユースケースで十分であるように見えます。ディレクトリを含むフォルダに切り替えてpwd -P
から、そのディレクトリの実際のパスを取得し、最後に元に戻します。
Perlにはreadlink関数があります(たとえば、Perlでシンボリックリンクをコピーするにはどうすればよいですか?)。これは、OSXを含むほとんどのプラットフォームで機能します。
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 Smithからの回答は、無限ループを示します。
これが私の答えです。これは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"
これは私が使用するものです:
stat -f %N $your_path
readlinkへのパスは、私のシステムとあなたのシステムで異なります。フルパスを指定してみてください。
/sw/sbin/readlink -f