Mac 上で GNU の readlink -f の動作を取得するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/1055671

  •  20-08-2019
  •  | 
  •  

質問

Linux では、 readlink ユーティリティはオプションを受け入れます -f 追加のリンクが続きます。これは Mac とおそらく BSD ベースのシステムでは機能しないようです。同等のものは何でしょうか?

デバッグ情報は次のとおりです。

$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
役に立ちましたか?

解決

readlink -f 次の 2 つのことを行います。

  1. 実際のファイルが見つかるまで、一連のシンボリックリンクに沿って反復処理が行われます。
  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 
}

これは完全な実装の完全な実装は、小さなシンボリックリンクサイクルのためのチェック、並びにマッサージ出力ビットを付加する。

最後に、パスを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")
}

それはそれだ、多かれ少なかれ。あなたのユースケースのためのユニットテストを持っていない任意のコードに依存して夢中になるだろうと、あなたのスクリプトに貼り付けるには十分シンプルですが、十分なトリッキーます。

  1. 自作をインストールする
  2. 「brew install coreutils」を実行します。
  3. 「greadlink -f path」を実行します。

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 ~のバージョンを持っています statNetBSD から派生したもの。

フォーマット スイッチを使用して出力を調整できます (上記のリンクにあるマニュアル ページを参照してください)。

%  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を使用する必要があるであろうこと。

私の仕事は、非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
}

オプションをチェックあなたが自動的にインストールするには、追加することができます自作する + <の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マークに近づいた真の「グローバル」であることを仮定します。

説明

coreutils は brew Mac OSX の実装に対応する GNU/Linux コア ユーティリティをインストールして、それらを使用できるようにするパッケージ

Mac osx システム上で、Linux coreutils (「コア ユーティリティ」) に似ているように見えても、いくつかの点 (フラグが異なるなど) が異なるプログラムやユーティリティを見つけることがあります。

これは、これらのツールの Mac OSX 実装が異なるためです。元の GNU/Linux のような動作を取得するには、 coreutils 経由でパッケージ化する brew パッケージ管理システム。

これにより、接頭辞が次の対応するコア ユーティリティがインストールされます。 g. 。例えば。のために readlink, 、対応するものが見つかります greadlink プログラム。

作るために readlink GNU のように実行する readlink (greadlink) の実装では、coreutils をインストールした後に簡単なエイリアスを作成できます。

実装

  1. 醸造をインストールする

次の手順に従ってください。 https://brew.sh/

  1. coreutils パッケージをインストールする

brew install coreutils

  1. エイリアスの作成

エイリアスは、~/.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"
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top