bashで可能な相対パスを展開する


82

私のスクリプトへの引数として、いくつかのファイルパスがあります。もちろん、それらは相対的(または〜を含む)にすることができます。しかし、私が書いた関数には、絶対的なパスが必要ですが、シンボリックリンクは解決されていません。

このための機能はありますか?



7
readlinkシンボリックリンクを解決します。OPはそれを望んでいません。
キーストンプソン



1
echo $(cd some_directory && pwd)、シンボリックリンクを解決しない、some_directoryが存在しない場合は失敗します。作業ディレクトリは影響を受けません。または変数に割り当てますMY_PATH=$(cd some_directory && pwd)
qeatzy 2017年

回答:


79

MY_PATH=$(readlink -f $YOUR_ARG)"./"およびのような相対パスを解決します"../"

これも考慮してください(ソース):

#!/bin/bash
dir_resolve()
{
cd "$1" 2>/dev/null || return $?  # cd to desired directory; if fail, quell any error messages but return exit status
echo "`pwd -P`" # output full, link-resolved path
}

# sample usage
if abs_path="`dir_resolve \"$1\"`"
then
echo "$1 resolves to $abs_path"
echo pwd: `pwd` # function forks subshell, so working directory outside function is not affected
else
echo "Could not reach $1"
fi

それは確かに素晴らしいことですが、問題はsimlinksが解決されることであり、それはユーザーとの混乱につながるでしょう。
laar 2011

man pwd:「-P物理的な現在の作業ディレクトリを表示します(すべてのシンボリックリンクが解決されました)」、-P
andsens 2013年

7
readlink -fOS Xで機能を取得するには、Homebrewを使用して実行し$ brew install coreutils ますgreadlink -f。次に、を使用できます。
jgpawletko 2015年

46

http://www.linuxquestions.org/questions/programming-9/bash-script-return-full-path-and-filename-680368/page3.htmlには次のものがあります

function abspath {
    if [[ -d "$1" ]]
    then
        pushd "$1" >/dev/null
        pwd
        popd >/dev/null
    elif [[ -e "$1" ]]
    then
        pushd "$(dirname "$1")" >/dev/null
        echo "$(pwd)/$(basename "$1")"
        popd >/dev/null
    else
        echo "$1" does not exist! >&2
        return 127
    fi
}

pushd/を使用popdして、pwd有用な状態になります。


2
悲しいことに、これは本当にここでの最良の答えであり、それが属する場所に投票されていません。OPの質問に正確に答えるための+1。彼はシンボリックリンクが解決されないことを望んでいDIRSTACKます。これは実際にはbashのローカルの問題であり、他には何もありません。いい解決策!
cptstubing06 2013年

5
標準のsh互換性を目指す場合は、pushd; cd <dir>; <cmd>; popdに置き換えることができます(cd <dir>; <cmd>)
Abbafei 2013年

16

シンプルなワンライナー:

function abs_path {
  (cd "$(dirname '$1')" &>/dev/null && printf "%s/%s" "$PWD" "${1##*/}")
}

使用法:

function do_something {
    local file=$(abs_path $1)
    printf "Absolute path to %s: %s\n" "$1" "$file"
}
do_something $HOME/path/to/some\ where

パスが存在するかどうかを完全に気にしないようにする方法をまだ模索しています(ファイルの作成時にも使用できます)。


1
これにより、現在の作業ディレクトリが変更されませんか?私はする必要がありcd -、その後、それをリセットするには?
devios1 2013年

4
必要なし。両親に注意してください、彼らは内部のコマンドをサブシェルで実行させます。サブシェルの状態(作業ディレクトリなど)は、その親シェルにリークしません。詳細はこちら:tldp.org/LDP/abs/html/subshel​​ls.html
andsens

1
素敵なワンライナー。ただし、問題があります。展開された$ 1の値にスラッシュがない場合(つまり、現在のディレクトリ内のファイル名である場合)、$ {1%/ *}はファイル名自体に展開され、cdコマンドは失敗します。代わりに$(dirname $ 1)を使用すると、「。」に展開されます。その場合。
パウロ

そうです、更新されました。私は交換するといくつかの引用問題を抱えてい${1##*/}basename、別の変数に入れず、しかしを、スペースでパスが正しく動作しません。
andsens 2014

1
@BryanP私は実際、私が維持しているdotfileシンクロナイザーのために完全に行き過ぎてしまいました。それはあなたがの終わりに相対パスを固執する場合は、不要なパスの部分を削除しclean_path()関数を持っている$PWD:そしてそれを通してそれを実行し、それは非常にうまく動作するはずgithub.com/andsens/homeshick/blob/...は
andsens

8

これは、OSXでの私にとってのトリックです。 $(cd SOME_DIRECTORY 2> /dev/null && pwd -P)

どこでも動作するはずです。他の解決策は複雑すぎるようでした。


4

OSXでは使用できます

stat -f "%N" YOUR_PATH

Linuxでは、realpath実行可能ファイルがあるかもしれません。そうでない場合は、以下が機能する可能性があります(リンクだけでなく)。

readlink -c YOUR_PATH

20
OSXの回答は機能しません。stat -f "%N" PATH私が与えたのとまったく同じ道を私に与えます。
リリーバラード

3

使用readlink -f <relative-path>、例えば

export FULLPATH=`readlink -f ./`

2

たぶん、これはより読みやすく、サブシェルを使用せず、現在のディレクトリを変更しません。

dir_resolve() {
  local dir=`dirname "$1"`
  local file=`basename "$1"`
  pushd "$dir" &>/dev/null || return $? # On error, return error code
  echo "`pwd -P`/$file" # output full, link-resolved path with filename
  popd &> /dev/null
}

1

別の方法があります。bashスクリプトにPythonの埋め込みを使用して、相対パスを解決できます。

abs_path=$(python3 - <<END
from pathlib import Path
path = str(Path("$1").expanduser().resolve())
print(path)
END
)

0

自己編集、私はちょうどOPが解決されたシンボリックリンクを探していないと言ったことに気づきました:

「しかし、私が書いた関数には、絶対的なパスが必要ですが、シンボリックリンクは解決されていません。」

結局、これは彼の質問にはそれほど適切ではないと思います。:)

私は何年にもわたってこれに何度も遭遇し、今回はOSXとLinuxで使用できる純粋なbashポータブルバージョンが必要だったので、先に進んで次のように書きました。

生きているバージョンはここに住んでいます:

https://github.com/keen99/shell-functions/tree/master/resolve_path

しかし、SOのために、これが現在のバージョンです(十分にテストされていると思いますが、フィードバックを受け付けています!)

プレーンボーンシェル(sh)で機能させるのは難しいことではないかもしれませんが、私は試しませんでした... $ FUNCNAMEが好きすぎます。:)

#!/bin/bash

resolve_path() {
    #I'm bash only, please!
    # usage:  resolve_path <a file or directory> 
    # follows symlinks and relative paths, returns a full real path
    #
    local owd="$PWD"
    #echo "$FUNCNAME for $1" >&2
    local opath="$1"
    local npath=""
    local obase=$(basename "$opath")
    local odir=$(dirname "$opath")
    if [[ -L "$opath" ]]
    then
    #it's a link.
    #file or directory, we want to cd into it's dir
        cd $odir
    #then extract where the link points.
        npath=$(readlink "$obase")
        #have to -L BEFORE we -f, because -f includes -L :(
        if [[ -L $npath ]]
         then
        #the link points to another symlink, so go follow that.
            resolve_path "$npath"
            #and finish out early, we're done.
            return $?
            #done
        elif [[ -f $npath ]]
        #the link points to a file.
         then
            #get the dir for the new file
            nbase=$(basename $npath)
            npath=$(dirname $npath)
            cd "$npath"
            ndir=$(pwd -P)
            retval=0
            #done
        elif [[ -d $npath ]]
         then
        #the link points to a directory.
            cd "$npath"
            ndir=$(pwd -P)
            retval=0
            #done
        else
            echo "$FUNCNAME: ERROR: unknown condition inside link!!" >&2
            echo "opath [[ $opath ]]" >&2
            echo "npath [[ $npath ]]" >&2
            return 1
        fi
    else
        if ! [[ -e "$opath" ]]
         then
            echo "$FUNCNAME: $opath: No such file or directory" >&2
            return 1
            #and break early
        elif [[ -d "$opath" ]]
         then 
            cd "$opath"
            ndir=$(pwd -P)
            retval=0
            #done
        elif [[ -f "$opath" ]]
         then
            cd $odir
            ndir=$(pwd -P)
            nbase=$(basename "$opath")
            retval=0
            #done
        else
            echo "$FUNCNAME: ERROR: unknown condition outside link!!" >&2
            echo "opath [[ $opath ]]" >&2
            return 1
        fi
    fi
    #now assemble our output
    echo -n "$ndir"
    if [[ "x${nbase:=}" != "x" ]]
     then
        echo "/$nbase"
    else 
        echo
    fi
    #now return to where we were
    cd "$owd"
    return $retval
}

brewのおかげで、これが典型的な例です。

%% ls -l `which mvn`
lrwxr-xr-x  1 draistrick  502  29 Dec 17 10:50 /usr/local/bin/mvn@ -> ../Cellar/maven/3.2.3/bin/mvn

この関数を使用すると、-real-パスが返されます。

%% cat test.sh
#!/bin/bash
. resolve_path.inc
echo
echo "relative symlinked path:"
which mvn
echo
echo "and the real path:"
resolve_path `which mvn`


%% test.sh

relative symlinked path:
/usr/local/bin/mvn

and the real path:
/usr/local/Cellar/maven/3.2.3/libexec/bin/mvn

0

OSがサポートしている場合は、次を使用します。

realpath "./some/dir"

そしてそれを変数で使用する:

some_path="$(realpath "./some/dir")"

それはあなたの道を拡大します。UbuntuとCentOSでテストされていますが、ご利用いただけない場合があります。readlinkを推奨する人もいますが、readlinkのドキュメントには次のように書かれています。

realpath(1)は、正規化機能に使用する推奨コマンドであることに注意してください。

なぜ変数を引用するのか不思議に思う人のために、パスのスペースを保持することです。行うrealpath some pathように、2つの異なるパス結果が得られます。しかし、realpath "some path"1つを返します。引用されたパラメータftw :)


-1

bashを排他的に使用する必要がありますか?私はこれを行う必要があり、LinuxとOS Xの違いにうんざりしていました。そのため、迅速で汚い解決策としてPHPを使用しました。

#!/usr/bin/php <-- or wherever
<?php
{
   if($argc!=2)
      exit();
   $fname=$argv[1];
   if(!file_exists($fname))
      exit();
   echo realpath($fname)."\n";
}
?>

私はそれがあまりエレガントな解決策ではないことを知っていますが、それは機能します。


これは、ファイルへのパスがすでにわかっている場合、またはファイルが現在のディレクトリにある場合に、ファイルへのパスを返すだけです... PATH設定でファイルが見つかりません
G-Man
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.