OS XでのBashスクリプトの絶対パス


98

OS Xで現在実行中のスクリプトへの絶対パスを取得しようとしています。

多くの返信が届くのを見ましたreadlink -f $0。ただし、OS X readlinkはBSDと同じであるため、機能しません(GNUのバージョンで機能します)。

これに対する標準のソリューションはありますか?





16
$( cd "$(dirname "$0")" ; pwd -P )
Jason S

回答:


88

realpath()仕事をするC関数がありますが、コマンドラインで利用できるものは何もありません。以下は、迅速かつダーティーな置き換えです。

#!/bin/bash

realpath() {
    [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

realpath "$0"

/。で始まるパスはそのまま出力されます。そうでない場合は、相対パスである必要があるため、前に付加さ$PWDれます。#./部分は取り除き./の正面から$1


1
C関数にも気付きましたが、対応するバイナリなどは見つかりませんでした。とにかく、あなたの関数は私のニーズにぴったり合っています。ありがとう!
The Mighty Rubber Duck

7
これはシンボリックリンクを逆参照しないことに注意してください。
Adam Vandenberg

17
realpath ../somethingリターン$PWD/../something
Lri

これは私にとってはうまくいきましたが、このbashスクリプトをLinuxシェルに送信する必要があり、「実際の」リアルパスと衝突したくないので、名前を「realpath_osx()」に変更しました。(もっとエレガントな方法があると確信していますが、私はbash n00bです。)
Andrew Theken 2012

8
command -v realpath >/dev/null 2>&1 || realpath() { ... }
Kara Brightwell

115

次の3つの簡単な手順で、この問題と他の多くのOS Xの問題を解決できます。

  1. Homebrewをインストールする
  2. brew install coreutils
  3. grealpath .

(3)をに変更できます。realpath(2)出力を参照してください


3
これで問題は解決しますが、必要としないもの、またはターゲットシステムにないものをインストールする必要があります。つまり、OS X
Jason S

6
@JasonS GNUのCoreutilsはあまりにも良いですではない使用します。それは多くの素晴らしいユーティリティが含まれています。実際、Linuxカーネルはそれなしでは役に立たないので、なぜそれをGNU / Linuxと呼ぶ人がいるのでしょう。Coreutilsは素晴らしいです。優れた答えこれは選択された答えでなければなりません。

7
@omouse質問は、「箱から出してすぐに使えるソリューション」に具体的に言及しています(OS Xの場合)。coreutilsの素晴らしさに関係なく、OS Xでは「そのまま」ではないので、答えは適切ではありません。これは、coreutilsがどの程度優れているか、またはOS Xの機能よりも優れているかどうかの問題ではあり$( cd "$(dirname "$0")" ; pwd -P )ません。「すぐに使える」ソリューションが私にとってはうまく機能します。
Jason S

2
OS Xの「機能」の1つは、ディレクトリとファイル名で大文字と小文字が区別されないことです。その結果、あなたが呼ばれるディレクトリを持っているなら、XXXそして誰かがcd xxxそれpwdから戻るでしょう.../xxx。この回答を除いて、上記のすべてのソリューションは、xxx本当に必要なものである場合に戻りますXXX。ありがとうございました!
Andrew

1
コードを無限にコピー、貼り付け、再発明することが既存のパッケージマネージャーソリューションよりも優れていることを知りません。必要な場合realpath、ほぼ確実に他のアイテムが必要になるとcoreutilsどうなりますか?これらの関数もbashで書き換えますか?:P
エゼキエルビクター

28

ああ。いくつかの理由で、以前の回答が少し欲しかったことがわかりました。特に、複数のレベルのシンボリックリンクを解決せず、非常に「Bash-y」です。元の質問では「Bashスクリプト」が明示的に求められますが、Mac OS XのBSD風の非GNUについても言及されていますreadlink。そこで、妥当な移植性(bashを 'sh'およびdashとして確認した)を試み、任意の数のシンボリックリンクを解決します。また、パス内の空白でも機能するはずですが、ユーティリティ自体のベース名に空白がある場合の動作はわかりません。

#!/bin/sh
realpath() {
  OURPWD=$PWD
  cd "$(dirname "$1")"
  LINK=$(readlink "$(basename "$1")")
  while [ "$LINK" ]; do
    cd "$(dirname "$LINK")"
    LINK=$(readlink "$(basename "$1")")
  done
  REALPATH="$PWD/$(basename "$1")"
  cd "$OURPWD"
  echo "$REALPATH"
}
realpath "$@"

誰かに役立つことを願っています。


1
localグローバル名前空間を汚染しないように、関数内で定義された変数にのみ使用することをお勧めします。例えばlocal OURPWD=...。少なくともbashでは機能します。
Michael Paesold

また、コードではプライベート変数に大文字を使用しないでください。大文字の変数はシステムで使用するために予約されています。
tripleee

スクリプトをありがとう。リンクと実際のファイルのベース名が異なる場合はBASENAME=$(basename "$LINK") 、while内にを追加して、2番目のLINKセッターとREALPATHセッターで使用することをお勧めします
stroborobo

これはシンボリックリンクを処理せず、..親参照はまったく同様に処理しrealpathます。homebrewがcoreutilsインストールされたら、ln -s /var/log /tmp/linkexample次に試してくださいrealpath /tmp/linkexample/../。これは印刷し/private/varます。ただし、シンボリックリンクではない/tmp/linkexample/..ため、代わりに関数が生成します..
Martijn Pieters

10

Pythonソリューションのよりコマンドラインに適したバリアント:

python -c "import os; print(os.path.realpath('$1'))"

2
念のため、誰もが1つのコマンドでPythonインタープリターを開始するのに十分な気が狂っている場合は…
Bachsau

私は十分に怒っていましたが、使用されましたpython -c "import os; import sys; print(os.path.realpath(sys.argv[1]))"
Alex Chamberlain

7

私はシステムプロビジョニングスクリプトで使用するためのソリューションを探していました。つまり、Homebrewがインストールされる前に実行します。適切なソリューションがないため、Perlなどのクロスプラットフォーム言語にタスクをオフロードします。

script_abspath=$(perl -e 'use Cwd "abs_path"; print abs_path(@ARGV[0])' -- "$0")

多くの場合、実際に必要なのは包含ディレクトリです。

here=$(perl -e 'use File::Basename; use Cwd "abs_path"; print dirname(abs_path(@ARGV[0]));' -- "$0")

すごい!Perlは少しオーバーヘッドがいいです!おそらく、最初のバージョンをに簡略化できFULLPATH=$(perl -e "use Cwd 'abs_path'; print abs_path('$0')")ます。何か理由がありますか?
Fペレイラ

@FPereiraエスケープされていないユーザー指定の文字列からプログラムコードを生成することは決して良い考えではありません。''防弾ではありません。$0たとえば、単一引用符が含まれている場合は壊れます。非常に簡単な例:でバージョンを試し、フルパスで/tmp/'/test.sh呼び出し/tmp/'/test.shます。
4ae1e1

あるいはもっと簡単です/tmp/'.sh
4ae1e1

6

他の人が指摘したように現実の道があるので:

// realpath.c
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char* argv[])
{
  if (argc > 1) {
    for (int argIter = 1; argIter < argc; ++argIter) {
      char *resolved_path_buffer = NULL;
      char *result = realpath(argv[argIter], resolved_path_buffer);

      puts(result);

      if (result != NULL) {
        free(result);
      }
    }
  }

  return 0;
}

Makefile:

#Makefile
OBJ = realpath.o

%.o: %.c
      $(CC) -c -o $@ $< $(CFLAGS)

realpath: $(OBJ)
      gcc -o $@ $^ $(CFLAGS)

次にコンパイルしmakeてソフトリンクを入れます:
ln -s $(pwd)/realpath /usr/local/bin/realpath


私たちはただ行うことができますgcc realpath.c -o /usr/local/bin/realpathか?
Alexander Mills

1
@AlexanderMillsコンパイラをルートとして実行しないでください。そうしないと、書き込み権限がありません/usr/local/bin
tripleee

6

Pythonを使用してそれを取得します。

#!/usr/bin/env python
import os
import sys

print(os.path.realpath(sys.argv[1]))

2

macOSでは、シンボリックリンクを確実に処理する唯一の解決策は、を使用することrealpathです。これにはが必要なのでbrew install coreutils、その手順を自動化しました。私の実装は次のようになります:

#!/usr/bin/env bash

set -e

if ! which realpath >&/dev/null; then
  if ! which brew >&/dev/null; then
    msg="ERROR: This script requires brew. See https://brew.sh for installation instructions."
    echo "$(tput setaf 1)$msg$(tput sgr0)" >&2
    exit 1
  fi
  echo "Installing coreutils/realpath"
  brew install coreutils >&/dev/null
fi

thisDir=$( dirname "`realpath "$0"`" )
echo "This script is run from \"$thisDir\""


このエラーは、brewインストールされていない場合に発生しますが、インストールすることもできます。私は、ネットから任意のルビコードを丸める何かを自動化することに抵抗を感じていました。

これはOleg Mikheevの回答の自動化されたバリエーションであることに注意してください。


重要なテスト

これらのソリューションの1つの優れたテストは次のとおりです。

  1. コードをスクリプトファイルのどこかに配置する
  2. 別のディレクトリで、ln -sそのファイルへのシンボリックリンク()
  3. そのシンボリックリンクからスクリプト実行します

ソリューションはシンボリックリンクを逆参照し、元のディレクトリを提供しますか?もしそうなら、それは動作します。


2
abs_path () {    
   echo "$(cd $(dirname "$1");pwd)/$(basename "$1")"
}

dirnameのディレクトリ名/path/to/file、つまり/path/to

cd /path/to; pwd パスが絶対パスであることを確認します。

basename/path/to/fileつまり、ファイル名だけがに表示されますfile


このコードは質問に答えることがありますが、このコードが質問に答える理由や方法に関する追加のコンテキストを提供すると、長期的な価値が向上します。
イゴールF.

1

上記のように、約6か月前にこれを撮影しました。似たようなものが再び必要になるまで、私はそれをすっかり忘れていました。それがどれほど初歩的であるかを知るのに私は 完全にショック受けました。私は約1年間、かなり集中的にコードを書くように自分自身を教えてきましたが、物事が最悪のときは何も何も学んでいないように感じることがよくあります。

上記の「解決策」は削除しますが、過去数か月の間に私が実際に学んだことを記録したものであることを本当に気に入っています。

しかし、私は余談です。私は座って、昨晩それをすべてしました。コメントの説明で十分です。私が作業を続けているコピーを追跡したい場合は、この要点をたどることができます。これはおそらく必要なことを行います。

#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.

## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
## dereference symbolic links (ala 'readlink') until the originating file
## is found. This is effectively the same function provided in stdlib.h as
## 'realpath' and on the command line in GNU 'readlink -f'.

## Neither of these tools, however, are particularly accessible on the many
## systems that do not have the GNU implementation of readlink, nor ship
## with a system compiler (not to mention the requisite knowledge of C).

## This script is written with portability and (to the extent possible, speed)
## in mind, hence the use of printf for echo and case statements where they
## can be substituded for test, though I've had to scale back a bit on that.

## It is (to the best of my knowledge) written in standard POSIX shell, and
## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
## issues with it, though I'm not sure why; so probably best to avoid for now.

## Particularly useful (in fact, the reason I wrote this) is the fact that
## it can be used within a shell script to find the path of the script itself.
## (I am sure the shell knows this already; but most likely for the sake of
## security it is not made readily available. The implementation of "$0"
## specificies that the $0 must be the location of **last** symbolic link in
## a chain, or wherever it resides in the path.) This can be used for some
## ...interesting things, like self-duplicating and self-modifiying scripts.

## Currently supported are three errors: whether the file specified exists
## (ala ENOENT), whether its target exists/is accessible; and the special
## case of when a sybolic link references itself "foo -> foo": a common error
## for beginners, since 'ln' does not produce an error if the order of link
## and target are reversed on the command line. (See POSIX signal ELOOP.)

## It would probably be rather simple to write to use this as a basis for
## a pure shell implementation of the 'symlinks' util included with Linux.

## As an aside, the amount of code below **completely** belies the amount
## effort it took to get this right -- but I guess that's coding for you.

##===-------------------------------------------------------------------===##

for argv; do :; done # Last parameter on command line, for options parsing.

## Error messages. Use functions so that we can sub in when the error occurs.

recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
errnoent(){ printf "No such file: "$@"\n" ;} # Borrow a horrible signal name.

# Probably best not to install as 'pathfull', if you can avoid it.

pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"

## 'test and 'ls' report different status for bad symlinks, so we use this.

 if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null;  then
    errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ];   then
    recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -z "$link" ]; then
    dangling 1>&2; exit 1; fi
 fi

## Not a link, but there might be one in the path, so 'cd' and 'pwd'.

 if [ -z "$link" ]; then if [ "$(dirname "$@" | cut -c1)" = '/' ]; then
   printf "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\n"; fi; exit 0
 fi

## Walk the symlinks back to the origin. Calls itself recursivly as needed.

 while [ "$link" ]; do
   cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
   case "$newlink" in
    "$link") dangling 1>&2 && exit 1                                       ;;
         '') printf "$(pwd)/$(basename "$link")\n"; exit 0                 ;;
          *) link="$newlink" && pathfull "$link"                           ;;
   esac
 done
 printf "$(pwd)/$(basename "$newlink")\n"
}

## Demo. Install somewhere deep in the filesystem, then symlink somewhere 
## else, symlink again (maybe with a different name) elsewhere, and link
## back into the directory you started in (or something.) The absolute path
## of the script will always be reported in the usage, along with "$0".

if [ -z "$argv" ]; then scriptname="$(pathfull "$0")"

# Yay ANSI l33t codes! Fancy.
 printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
 printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         \033[4m$scriptname\033[24m\n\n        "
 printf " From within an invocation of a script, locate the script's "
 printf "own file\n         (no matter where it has been linked or "
 printf "from where it is being called).\n\n"

else pathfull "$@"
fi

1
要旨リンクが壊れているようです。
Alex

この答えは機能します:stackoverflow.com/a/46772980/1223975 ....そのまま使用する必要があります。
Alexander Mills

実装は..親参照の前にシンボリックリンクを解決しないことに注意してください。たとえば、シンボリックリンクが指すパスの親ではなく/foo/link_to_other_directory/..解決されます。そして、ルートから開始して、まだ処理されている残りの部分への先頭のリンクターゲットを更新する各パスコンポーネントを解決します。この質問への回答を追加して、そのロジックを再実装します。/foo/foo/link_to_other_directoryreadlink -frealpath
Martijn Pieters

1

Mac OS Xのrealpath

realpath() {
    path=`eval echo "$1"`
    folder=$(dirname "$path")
    echo $(cd "$folder"; pwd)/$(basename "$path"); 
}

関連パスの例:

realpath "../scripts/test.sh"

ホームフォルダーの例

realpath "~/Test/../Test/scripts/test.sh"

1
でそれを呼び出すときに素敵なシンプルなソリューション、私はちょうど、1回の警告を見つけました..:指定されたパスがディレクトリである場合、私はチェックを追加しましたので、それは、正しい答えを生成しません if test -d $path ; then echo $(cd "$path"; pwd) ; else [...]
ハーバート

うまく"$(dirname $(dirname $(realpath $0)))"いきませんでしたが、うまくいきました。何か他のものが必要です...
Alexander Mills

無用な使用echoも犯されるべきではない。
Tripleee、

これは実際には、realpathが行うような方法でシンボリックリンクを解決しません。シンボリックリンクを解決するではなく、解決する前に..親の参照解決します。homebrewをcoreutilsインストールしてこれを試して、リンクを作成してln -s /var/log /tmp/linkexample実行しrealpath /tmp/linkexample/../ます。これは印刷し/private/varます。ただし、CDの後に表示される/tmp/linkexample/..ため、代わりに関数が生成されます。pwd/tmp/linkexample
Martijn Pieters

0

これはOSXで機能するようで、バイナリは必要なく、ここからプルされました

function normpath() {
  # Remove all /./ sequences.
  local path=${1//\/.\//\/}

  # Remove dir/.. sequences.
  while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
    path=${path/${BASH_REMATCH[0]}/}
  done
  echo $path
}

0

私はこれが好き:

#!/usr/bin/env bash
function realpath() {
    local _X="$PWD"
    local _LNK=$1
    cd "$(dirname "$_LNK")"
    if [ -h "$_LNK" ]; then
        _LNK="$(readlink "$_LNK")"
        cd "$(dirname "$_LNK")"
    fi
    echo "$PWD/$(basename "$_LNK")"
    cd "$_X"
}

0

realpathOS Xでの置換が必要でした。これは、と同様にreadlink -f、シンボリックリンクと親参照を持つパスで正しく動作するものです。これには、親参照を解決するにパス内のシンボリックリンクを解決することが含まれます。たとえば、自作のcoreutilsボトルをインストールした場合は、次のコマンドを実行します。

$ ln -s /var/log/cups /tmp/linkeddir  # symlink to another directory
$ greadlink -f /tmp/linkeddir/..      # canonical path of the link parent
/private/var/log

親dir参照を解決する前に解決readlink -fされたこと/tmp/linkeddir 注意してください..。もちろん、readlink -fMacにありません。

そのため、bash実装の一部としてrealpath、GNUlib canonicalize_filename_mode(path, CAN_ALL_BUT_LAST)関数呼び出しが行うことをBash 3.2 に再実装しました。これは、GNU readlink -fが行う関数呼び出しでもあります。

# shellcheck shell=bash
set -euo pipefail

_contains() {
    # return true if first argument is present in the other arguments
    local elem value

    value="$1"
    shift

    for elem in "$@"; do 
        if [[ $elem == "$value" ]]; then
            return 0
        fi
    done
    return 1
}

_canonicalize_filename_mode() {
    # resolve any symlink targets, GNU readlink -f style
    # where every path component except the last should exist and is
    # resolved if it is a symlink. This is essentially a re-implementation
    # of canonicalize_filename_mode(path, CAN_ALL_BUT_LAST).
    # takes the path to canonicalize as first argument

    local path result component seen
    seen=()
    path="$1"
    result="/"
    if [[ $path != /* ]]; then  # add in current working dir if relative
        result="$PWD"
    fi
    while [[ -n $path ]]; do
        component="${path%%/*}"
        case "$component" in
            '') # empty because it started with /
                path="${path:1}" ;;
            .)  # ./ current directory, do nothing
                path="${path:1}" ;;
            ..) # ../ parent directory
                if [[ $result != "/" ]]; then  # not at the root?
                    result="${result%/*}"      # then remove one element from the path
                fi
                path="${path:2}" ;;
            *)
                # add this component to the result, remove from path
                if [[ $result != */ ]]; then
                    result="$result/"
                fi
                result="$result$component"
                path="${path:${#component}}"
                # element must exist, unless this is the final component
                if [[ $path =~ [^/] && ! -e $result ]]; then
                    echo "$1: No such file or directory" >&2
                    return 1
                fi
                # if the result is a link, prefix it to the path, to continue resolving
                if [[ -L $result ]]; then
                    if _contains "$result" "${seen[@]+"${seen[@]}"}"; then
                        # we've seen this link before, abort
                        echo "$1: Too many levels of symbolic links" >&2
                        return 1
                    fi
                    seen+=("$result")
                    path="$(readlink "$result")$path"
                    if [[ $path = /* ]]; then
                        # if the link is absolute, restart the result from /
                        result="/"
                    elif [[ $result != "/" ]]; then
                        # otherwise remove the basename of the link from the result
                        result="${result%/*}"
                    fi
                elif [[ $path =~ [^/] && ! -d $result ]]; then
                    # otherwise all but the last element must be a dir
                    echo "$1: Not a directory" >&2
                    return 1
                fi
                ;;
        esac
    done
    echo "$result"
}

これには循環シンボリックリンク検出が含まれ、同じ(中間)パスが2回見られると終了します。

必要なものがすべての場合はreadlink -f、上記を次のように使用できます。

readlink() {
    if [[ $1 != -f ]]; then  # poor-man's option parsing
        # delegate to the standard readlink command
        command readlink "$@"
        return
    fi

    local path result seenerr
    shift
    seenerr=
    for path in "$@"; do
        # by default readlink suppresses error messages
        if ! result=$(_canonicalize_filename_mode "$path" 2>/dev/null); then
            seenerr=1
            continue
        fi
        echo "$result"
    done
    if [[ $seenerr ]]; then
        return 1;
    fi
}

以下のためにrealpath、私も必要--relative-to--relative-basecanonicalizing後にあなたに相対パスを与えている、サポート:

_realpath() {
    # GNU realpath replacement for bash 3.2 (OS X)
    # accepts --relative-to= and --relative-base options
    # and produces canonical (relative or absolute) paths for each
    # argument on stdout, errors on stderr, and returns 0 on success
    # and 1 if at least 1 path triggered an error.

    local relative_to relative_base seenerr path

    relative_to=
    relative_base=
    seenerr=

    while [[ $# -gt 0 ]]; do
        case $1 in
            "--relative-to="*)
                relative_to=$(_canonicalize_filename_mode "${1#*=}")
                shift 1;;
            "--relative-base="*)
                relative_base=$(_canonicalize_filename_mode "${1#*=}")
                shift 1;;
            *)
                break;;
        esac
    done

    if [[
        -n $relative_to
        && -n $relative_base
        && ${relative_to#${relative_base}/} == "$relative_to"
    ]]; then
        # relative_to is not a subdir of relative_base -> ignore both
        relative_to=
        relative_base=
    elif [[ -z $relative_to && -n $relative_base ]]; then
        # if relative_to has not been set but relative_base has, then
        # set relative_to from relative_base, simplifies logic later on
        relative_to="$relative_base"
    fi

    for path in "$@"; do
        if ! real=$(_canonicalize_filename_mode "$path"); then
            seenerr=1
            continue
        fi

        # make path relative if so required
        if [[
            -n $relative_to
            && ( # path must not be outside relative_base to be made relative
                -z $relative_base || ${real#${relative_base}/} != "$real"
            )
        ]]; then
            local common_part parentrefs

            common_part="$relative_to"
            parentrefs=
            while [[ ${real#${common_part}/} == "$real" ]]; do
                common_part="$(dirname "$common_part")"
                parentrefs="..${parentrefs:+/$parentrefs}"
            done

            if [[ $common_part != "/" ]]; then
                real="${parentrefs:+${parentrefs}/}${real#${common_part}/}"
            fi
        fi

        echo "$real"
    done
    if [[ $seenerr ]]; then
        return 1
    fi
}

if ! command -v realpath > /dev/null 2>&1; then
    # realpath is not available on OSX unless you install the `coreutils` brew
    realpath() { _realpath "$@"; }
fi

このコードのコードレビューリクエストに単体テストを含めました。


-2

コメンターとのコミュニケーションに基づいて、私はそれが非常に困難であり、Ubuntuとまったく同じように動作するリアルパスを実装するための小さな方法がないことに同意しました。

しかし、次のバージョンは、MacBookでの私の日常的なニーズを満たすことができず、最良の答えを得ることができない、コーナーケースを処理できます。このコードを〜/ .bashrcに入れて覚えてください:

  • argに指定できるのは1つのファイルまたはディレクトリのみで、ワイルドカードは使用できません
  • dirまたはファイル名にスペースを含めない
  • 少なくともファイルまたはディレクトリの親ディレクトリが存在する
  • お気軽にご利用ください。.. /事、これらは安全です

    # 1. if is a dir, try cd and pwd
    # 2. if is a file, try cd its parent and concat dir+file
    realpath() {
     [ "$1" = "" ] && return 1

     dir=`dirname "$1"`
     file=`basename "$1"`

     last=`pwd`

     [ -d "$dir" ] && cd $dir || return 1
     if [ -d "$file" ];
     then
       # case 1
       cd $file && pwd || return 1
     else
       # case 2
       echo `pwd`/$file | sed 's/\/\//\//g'
     fi

     cd $last
    }

無用な使用echoを避けたい。ただ、pwd同じ行いecho $(pwd)シェルの2番目のコピーを起動する必要はありません。また、引数を引用符で囲まないのechoはバグです(先頭または末尾の空白、隣接する内部の空白文字が失われ、ワイルドカードが展開されるなど)。詳細については、stackoverflow.com
questions / 10067266 /…を

また、存在しないパスの動作にはバグがあります。しかし、それが「しかし、覚えている」という文がおそらく言っていることだと思います。Ubuntuでの動作は確かrealpathに、存在しないディレクトリを要求したときに現在のディレクトリを印刷することではありません。
Tripleee、

一貫性を保つために、おそらくdir=$(dirname "$1"); file=$(basename "$1")陳腐化したバックティック構文の代わりに使用することをお勧めします。また、引数の正しい引用にも注意してください。
Tripleee

あなたの更新された答えは多くのバグを修正するのに失敗するようで、新しいものを追加します。
Tripleee、

私がubuntu 18.04デスクトップで行うすべてのテストで問題ないので、特定の失敗したケースをいくつか教えてください。
オシア
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.