428

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

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

$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
4

26 に答える 26

476

MacPortsとHomebrewは、(GNU readlink)を含むcoreutilsパッケージを提供します。greadlinkmackb.comのMichaelKallweitt投稿のクレジット。

brew install coreutils

greadlink -f file.txt
于 2010-10-27T09:05:05.400 に答える
198

readlink -f2つのことを行います:

  1. 実際のファイルが見つかるまで、一連のシンボリックリンクに沿って繰り返されます。
  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

于 2009-07-12T20:51:37.750 に答える
48

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バージョンは短くする必要があります。:)

于 2009-07-12T01:11:34.190 に答える
39

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)"
于 2014-07-04T10:28:22.643 に答える
29

私はさらに別の実装を積み重ねるのは嫌いですが、このようなもののエッジケースの数は重要であるため、a)ポータブルで純粋なシェル実装、およびb)ユニットテストカバレッジが必要でした。

テストと完全なコードについては、Githubの私のプロジェクトを参照してください。以下は、実装の概要です。

キース・スミスが鋭く指摘しているように、readlink -f2つのことを行います: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")
}

多かれ少なかれそれだけです。スクリプトに貼り付けるのに十分シンプルですが、ユースケースの単体テストがないコードに依存することに夢中になるほどトリッキーです。

于 2014-04-09T18:34:58.820 に答える
28
  1. 自作をインストールする
  2. 「brewinstallcoreutils」を実行します
  3. 「greadlink-fpath」を実行します

greadlinkは、-fを実装するgnureadlinkです。Macportsなども使用できます。自作が好きです。

于 2012-03-29T02:35:28.667 に答える
22

私はrealpathと呼ばれるスクリプトを個人的に作成しました。これは、次のようになります。

#!/usr/bin/env python
import os, sys
print(os.path.realpath(sys.argv[1]))
于 2009-11-05T06:05:40.377 に答える
15

これはどうですか?

function readlink() {
  DIR="${1%/*}"
  (cd "$DIR" && echo "$(pwd -P)")
}
于 2012-02-28T14:44:40.597 に答える
11

私のために働く怠惰な方法、

$ brew install coreutils
$ ln -s /usr/local/bin/greadlink /usr/local/bin/readlink
$ which readlink
/usr/local/bin/readlink
/usr/bin/readlink
于 2020-01-27T21:11:27.130 に答える
9

実装

  1. brewをインストールします

https://brew.sh/の指示に従ってください

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

brew install coreutils

  1. エイリアスまたはシンボリックリンクを作成する

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プログラムがあります。

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

于 2019-05-20T11:22:30.047 に答える
7

FreeBSDOSXstatには、 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

動作します。

于 2014-09-17T01:01:58.010 に答える
7

この問題を解決し、HomebrewがインストールされたMacまたはFreeBSDでreadlinkの機能を有効にする最も簡単な方法は、「c​​oreutils」パッケージをインストールすることです。特定のLinuxディストリビューションおよびその他のPOSIXOSでも必要になる場合があります。

たとえば、FreeBSD 11では、次のコマンドを呼び出してインストールしました。

# pkg install coreutils

Homebrewを使用するMacOSでは、コマンドは次のようになります。

$ brew install coreutils

他の答えがなぜそれほど複雑なのかよくわかりません。それだけです。ファイルは別の場所にありません。まだインストールされていないだけです。

于 2017-01-06T21:28:04.253 に答える
6

これは、 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スタイル)ライセンスの条件の下でライセンスされています。ライセンスのコピーは、上記の私のサイトへのハイパーリンクをたどることで見つけることができます。

于 2010-04-26T00:10:54.297 に答える
3

更新を開始

これは非常に頻繁な問題であるため、 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互換の終了コードを返します。

于 2013-10-08T14:43:46.387 に答える
3

すでにたくさんの答えがありますが、どれも私にはうまくいきませんでした...だからこれが私が今使っているものです。

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"
}
于 2014-10-13T08:44:06.587 に答える
3

私の仕事は非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のマークに近づいています。

于 2019-01-21T07:27:45.557 に答える
2

readlink -fPOSIXシェルスクリプトの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
}

最新のコードを参照してください。修正される場合があります。

于 2020-03-07T17:14:02.830 に答える
1

遅くなるよりはましだと思います。私のFedoraスクリプトがMacで動作していなかったので、私はこれを開発することに動機付けられました。問題は依存関係とBashです。Macにはそれらがないか、ある場合は別の場所(別のパス)にあることがよくあります。クロスプラットフォームのBashスクリプトでの依存関係パスの操作は、せいぜい頭痛の種であり、最悪の場合はセキュリティリスクです。したがって、可能であれば、それらの使用を避けるのが最善です。

以下の関数get_realpath()は単純で、Bash中心であり、依存関係は必要ありません。私はBashビルトインのechocdのみを使用しています。また、すべてが途中の各段階でテストされ、エラー状態が返されるため、かなり安全です。

シンボリックリンクをたどりたくない場合は、スクリプトの先頭に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以降が必要ですが、古いバージョンがまだ存在する場合は、それらで動作するようにすることができます。

于 2013-10-03T15:48:13.960 に答える
1

echo $(cd $(dirname file1); pwd -P)

于 2022-01-06T08:58:14.180 に答える
0

と同じ結果を提供できる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

于 2014-11-08T14:55:02.573 に答える
0

本当にプラットフォームに依存しないのは、このR-onlinerでもあります

readlink(){ RScript -e "cat(normalizePath(commandArgs(T)[1]))" "$1";}

実際に模倣するにはreadlink -f <path>、$1ではなく$2を使用する必要があります。

于 2015-12-10T20:17:01.780 に答える
0

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から、そのディレクトリの実際のパスを取得し、最後に元に戻します。

于 2021-02-09T01:57:40.787 に答える
-2

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
于 2011-08-27T11:59:18.560 に答える
-2

@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"
于 2014-09-24T15:53:48.983 に答える
-3

これは私が使用するものです:

stat -f %N $your_path

于 2015-12-23T19:23:45.683 に答える
-5

readlinkへのパスは、私のシステムとあなたのシステムで異なります。フルパスを指定してみてください。

/sw/sbin/readlink -f

于 2009-06-30T13:46:02.577 に答える