相対パスを指定して絶対パスを取得する方法


159

相対パスを指定して絶対パスを取得するコマンドはありますか?

たとえば、$ lineにdir内の各ファイルの絶対パスを含めたい ./etc/

find ./ -type f | while read line; do
   echo $line
done


1
完全には複製されていませんが、類似しています
mpapis 2010年


これまでにリストされたどのソリューションよりもはるかに優れたソリューションは、ここでhow-to-convert-relative-path-to-absolute-path-in-unix
Daniel Genin

回答:


66

使用する:

find "$(pwd)"/ -type f

すべてのファイルを取得するか

echo "$(pwd)/$line"

絶対パスを表示する(相対パスが重要な場合)


2
検索で相対パスを指定するとどうなりますか?
nubme 2010年

17
次に、質問へのコメント内のリンクを確認します。おそらく「readlink -f $ line」
mpapis

3
$(pwd)代わりに使用する理由$PWD?(はい、私pwdは組み込みであることを知っています)
アダム・カッツ

178

お試しくださいrealpath

~ $ sudo apt-get install realpath  # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc

シンボリックリンクの拡張を回避するには、を使用しますrealpath -s

答えは、「ファイルへの絶対パスを出力するbash / fishコマンドにあります


11
realpathMac(OS X 10.11 "El Capitan")では利用できないようです。:-(
Laryx Decidua 16

realpathCentOS 6でも利用できないようです
user5359531 '27年

6
osxでは、次のものbrew install coreutilsが組み込まれますrealpath
Kevin Chen

私のUbuntu 18.04では、realpathすでに存在しています。個別にインストールする必要はありませんでした。
Acumenus

驚いたことに、realpathGit for Windowsで利用可能です(少なくとも私にとっては)。
Andrew Keeton、

104

coreutilsパッケージがインストールされている場合、通常readlink -f relative_file_nameは絶対パッケージを取得するために使用できます(すべてのシンボリックリンクが解決されています)


3
この動作は、ユーザーの要求とは少し異なり、パス内の任意の場所で再帰的なシンボリックリンクをたどって解決します。場合によってはそれを望まないかもしれません。
2016年

1
@BradPeabodyこれは、homebrewからcoreutilsをインストールする場合、Macで機能しますbrew install coreutils。:しかし、実行可能ファイルはAGによって付加されるgreadlink -f relative_file_name
ミゲル・イスラ

2
readlink(1)のマニュアルページには、その説明の最初の文として「realpath(1)が正規化機能に使用する推奨コマンドであることに注意してください。」と記載されています。
josch

1
-fの代わりに-eを使用して、ファイル/ディレクトリが存在するかどうかを確認できます
Iceman

69
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"

UPDいくつかの説明

  1. このスクリプトは、引数として相対パスを取得します "$1"
  2. 次に、そのパスのdirname部分を取得します(dirまたはfileをこのスクリプトに渡すことができます)。dirname "$1"
  3. 次にcd "$(dirname "$1")、この相対ディレクトリに移動し、pwdシェルコマンドを実行して絶対パスを取得します
  4. その後、絶対パスにベース名を追加します。$(basename "$1")
  5. 最後のステップは、私たちのようecho

8
この回答は、最高のBashイディオムを使用しています
Rondo

2
readlinkはLinuxのシンプルなソリューションですが、このソリューションはOSXでも機能するため、+ 1
thetoolmanは

1
このスクリプトは、what realpathまたはreadlink -fdo と同等ではありません。たとえば、最後のコンポーネントがシンボリックリンクであるパスでは機能しません。
josch

2
@josch:問題はシンボリックリンクの解決についてではありません。しかし、あなたは、あなたが提供できることを行いたい場合-Pにオプションをpwdコマンド:echo "$(cd "$(dirname "$1")"; pwd -P)/$(basename "$1")"
オイゲン・Konkov

4
私は答えが好きですが、ユーザーがディレクトリへのcdを許可されている場合にのみ機能します。これは常に可能であるとは限りません。
Matthias B

34

価値のあるものについて、私は選ばれた答えに投票しましたが、解決策を共有したいと思いました。欠点は、それがLinuxのみであることです。スタックオーバーフローが発生する前に、OSXに相当するものを探すのに5分ほどかかりました。確かにあると思います。

Linuxではreadlink -e、と組み合わせて使用できますdirname

$(dirname $(readlink -e ../../../../etc/passwd))

収量

/etc/

次にdirname、の姉妹を使用してbasename、ファイル名を取得します

$(basename ../../../../../passwd)

収量

passwd

全部まとめて

F=../../../../../etc/passwd
echo "$(dirname $(readlink -e $F))/$(basename $F)"

収量

/etc/passwd

ディレクトリをターゲットにしている場合は安全であり、basename何も返さず、最終出力に2つのスラッシュが含まれるだけです。


優秀なのエントリdirnamereadlinkbasename。これにより、ターゲットではなく、シンボリックリンクの絶対パスを取得することができました。
kevinarpe 2013年

シンボリックリンクへのパスを返すときに機能しません(たまたま必要なのは...)。
トマーシュZato -復活モニカ

存在しないパスへの絶対パスをどのように見つけることができますか?
synthesizerpatel

@synthesizerpatelかなり簡単に、私は思ったでしょう。私がいるときに/home/GKFXtouch newfile入力した場合、Enterキーを押す前に、「create / home / GKFX / newfile」を意味することを理解できます。これは、まだ存在しないファイルへの絶対パスです。
GKFX 2017

25

これが最もポータブルだと思います:

abspath() {                                               
    cd "$(dirname "$1")"
    printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
    cd "$OLDPWD"
}

パスが存在しない場合は失敗します。


3
再度CDに戻す必要はありません。stackoverflow.com/a/21188136/1504556を参照してください。あなたにとっては、このページでの最良の答えです。興味のある人のために、リンクはこのソリューションが機能する理由についての説明を提供します。
peterh 2014年

これはあまり移植性がなくdirname、GNUコアユーティリティであり、私が信じているすべてのunixenに共通するわけではありません。
einpoklum 2017年

@einpoklum dirnameはPOSIX標準ユーティリティです。ここを参照してください:pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html
Ernest A

ああ、神様、ありがとうございます。私は${1##*/}1日間使用するバージョンを修正しようとしていましたが、そのゴミを置き換えたのでbasename "$1"、最後に/で終わるパスを適切に処理しているようです。
l3l_aze

NB、これで終わるパスでは正しいことを行いません../..
Alex Coventry

16

realpath おそらく最高です

だが ...

最初の質問は、最初は非常に混乱していましたが、前述のように、質問との関連性が低い例がありました。

選択された回答は実際には与えられた例に回答し、タイトルの質問にはまったく回答しません。最初のコマンドはその答えです(それは本当ですか?疑問です)。 '/'がなくても同じことができます。そして、2番目のコマンドが何をしているのかわかりません。

いくつかの問題が混在しています:

  • 相対パス名を絶対パス名に変更します。それが何を示していても、おそらく何もしません。(通常、などのコマンドを発行する場合、ファイルが実際に作成される前touch foo/barに、パス名foo/barが存在し、計算で使用される可能性があります。

  • 特にパス上のシンボリックリンク(シンボリックリンク)が原因で、同じファイル(または潜在的なファイル)を示すいくつかの絶対パス名が存在する可能性がありますが、他の理由(デバイスが読み取り専用として2倍にマウントされている可能性があります)。そのようなシンボリックリンクを明示的に解決したい場合とそうでない場合があります。

  • 非シンボリックファイルまたは名前へのシンボリックリンクのチェーンの最後に到達する。これにより、絶対パス名が生成される場合と生成されない場合があります。そして、それを絶対パス名に解決したい場合とそうでない場合があります。

readlink fooオプションのないコマンドfooは、その引数がシンボリックリンクであり、その回答がそのシンボリックリンクの値である場合にのみ回答を提供します。他のリンクはたどられません。答えは相対パスかもしれません:symlink引数の値は何でも。

ただし、readlinkすべてのファイルで機能するオプション(-f -eまたは-m)があり、実際に引数で示されるファイルに1つの絶対パス名(シンボリックリンクのないもの)を指定します。

これは、シンボリックリンクでないものには問題なく機能しますが、パスの中間シンボリックリンクを解決せずに絶対パス名を使用したい場合があります。これはコマンドによって行われますrealpath -s foo

シンボリックリンク引数の場合readlink、そのオプションを使用すると、引数への絶対パス上のすべてのシンボリックリンクが再度解決されますが、引数値の後に発生する可能性のあるすべてのシンボリックリンクも含まれます。引数のリンク先ではなく、引数のシンボリックリンク自体への絶対パスが必要な場合は、そうしたくないかもしれません。ここでも、fooシンボリックリンクの場合realpath -s foo、引数として指定されたものを含め、シンボリックリンクを解決せずに絶対パスを取得します。

なければ-sオプション、realpathほとんどのように同じことを readlink単にリンクの価値だけでなく、いくつかの他のものを読んだ以外、。なぜreadlinkそのオプションがあり、明らかにで望ましくない冗長性が生じるの か、私にははっきりしませんrealpath

システムを横断していくつかのバリエーションがあるかもしれないことを除いて、ウェブを探索することはそれほど多くを語りません。

結論:realpath少なくともここで要求された用途に対して、最も柔軟性が高く、使用するのに最適なコマンドです。


10

他のユーティリティ(coreutilsパッケージ)の存在を示唆していないため私のお気に入りのソリューションは@EugenKonkovによるものでした。

しかし、相対パス "。"は失敗しました。および「..」なので、これらの特殊なケースを処理するわずかに改善されたバージョンを次に示します。

cdただし、ユーザーが相対パスの親ディレクトリにアクセスする権限を持っていない場合でも、失敗します。

#! /bin/sh

# Takes a path argument and returns it as an absolute path. 
# No-op if the path is already absolute.
function to-abs-path {
    local target="$1"

    if [ "$target" == "." ]; then
        echo "$(pwd)"
    elif [ "$target" == ".." ]; then
        echo "$(dirname "$(pwd)")"
    else
        echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
    fi
}

7

オイゲンの答えは私にとってはうまくいきませんでしたが、これはうまくいきました:

    absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"

ちなみに、現在の作業ディレクトリは影響を受けません。


注意:それはスクリプトです。ここで$ 1は最初の引数です
Eugen Konkov 2017年

5

最善の解決策であるimhoは、https://stackoverflow.com/a/3373298/9724628に投稿されているものです。

動作にはpythonが必要ですが、エッジケースのすべてまたはほとんどをカバーしており、非常に移植可能なソリューションであるようです。

  1. シンボリックリンクを解決すると:
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
  1. またはそれなし:
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file

3

の場合、find検索する絶対パスを指定するのがおそらく最も簡単です。例:

find /etc
find `pwd`/subdir_of_current_dir/ -type f

3

realpathが存在せず、readlinkが絶対パスを出力できないMac OS Xでbashを使用している場合は、独自のバージョンをコード化して出力することができます。これが私の実装です:

(ピュアバッシュ)

abspath(){
  local thePath
  if [[ ! "$1" =~ ^/ ]];then
    thePath="$PWD/$1"
  else
    thePath="$1"
  fi
  echo "$thePath"|(
  IFS=/
  read -a parr
  declare -a outp
  for i in "${parr[@]}";do
    case "$i" in
    ''|.) continue ;;
    ..)
      len=${#outp[@]}
      if ((len==0));then
        continue
      else
        unset outp[$((len-1))] 
      fi
      ;;
    *)
      len=${#outp[@]}
      outp[$len]="$i"
      ;;
    esac
  done
  echo /"${outp[*]}"
)
}

(gawkを使用)

abspath_gawk() {
    if [[ -n "$1" ]];then
        echo $1|gawk '{
            if(substr($0,1,1) != "/"){
                path = ENVIRON["PWD"]"/"$0
            } else path = $0
            split(path, a, "/")
            n = asorti(a, b,"@ind_num_asc")
            for(i in a){
                if(a[i]=="" || a[i]=="."){
                    delete a[i]
                }
            }
            n = asorti(a, b, "@ind_num_asc")
            m = 0
            while(m!=n){
                m = n
                for(i=1;i<=n;i++){
                    if(a[b[i]]==".."){
                        if(b[i-1] in a){
                            delete a[b[i-1]]
                            delete a[b[i]]
                            n = asorti(a, b, "@ind_num_asc")
                            break
                        } else exit 1
                    }
                }
            }
            n = asorti(a, b, "@ind_num_asc")
            if(n==0){
                printf "/"
            } else {
                for(i=1;i<=n;i++){
                    printf "/"a[b[i]]
                }
            }
        }'
    fi
}

(純粋なbsd awk)

#!/usr/bin/env awk -f
function abspath(path,    i,j,n,a,b,back,out){
  if(substr(path,1,1) != "/"){
    path = ENVIRON["PWD"]"/"path
  }
  split(path, a, "/")
  n = length(a)
  for(i=1;i<=n;i++){
    if(a[i]==""||a[i]=="."){
      continue
    }
    a[++j]=a[i]
  }
  for(i=j+1;i<=n;i++){
    delete a[i]
  }
  j=0
  for(i=length(a);i>=1;i--){
    if(back==0){
      if(a[i]==".."){
        back++
        continue
      } else {
        b[++j]=a[i]
      }
    } else {
      if(a[i]==".."){
        back++
        continue
      } else {
        back--
        continue
      }
    }
  }
  if(length(b)==0){
    return "/"
  } else {
    for(i=length(b);i>=1;i--){
      out=out"/"b[i]
    }
    return out
  }
}

BEGIN{
  if(ARGC>1){
    for(k=1;k<ARGC;k++){
      print abspath(ARGV[k])
    }
    exit
  }
}
{
  print abspath($0)
}

例:

$ abspath I/am/.//..//the/./god/../of///.././war
/Users/leon/I/the/war

1

find $PWDまたは(bashで)を除いて、彼らが言ったことfind ~+は少し便利です。


1

@ ernest-aの回答に似てい$OLDPWDますが、新しい関数に影響を与えたり、定義したりせずに、サブシェルを起動できます(cd <path>; pwd)

$ pwd
/etc/apache2
$ cd ../cups 
$ cd -
/etc/apache2
$ (cd ~/..; pwd)
/Users
$ cd -
/etc/cups

1

相対パスがディレクトリパスである場合、私のものを試してください、最も良いはずです:

absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)

echo $absPath

このソリューションはbashでのみ機能します。stackoverflow.com/ a / 5193087/712014
Michael

1
echo "mydir/doc/ mydir/usoe ./mydir/usm" |  awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'

3
このコードスニペットをありがとうございます。このコードスニペットは、限られた短期間のヘルプを提供する可能性があります。適切な説明これがなぜ問題の優れた解決策であるを示すことにより、長期的な価値を大幅に改善し、他の同様の質問を持つ将来の読者にとってさらに役立つでしょう。回答を編集して、仮定を含めて説明を追加してください。
Toby Speight

1

@ ernest-aのかなり良いバージョンの改善:

absolute_path() {
    cd "$(dirname "$1")"
    case $(basename $1) in
        ..) echo "$(dirname $(pwd))";;
        .)  echo "$(pwd)";;
        *)  echo "$(pwd)/$(basename $1)";;
    esac
}

正しくパスの最後の要素がある場合と、この取引..その場合には、"$(pwd)/$(basename "$1")"中@アーネスト-Aの答えのように通ってくるだろうaccurate_sub_path/spurious_subdirectory/..


0

相対パスを含む変数を絶対パスに変換したい場合、これは機能します:

   dir=`cd "$dir"`

「cd」は、ここではサブシェルで実行されるため、作業ディレクトリを変更せずにエコーします。


2
bash-4.3-p46では、これは機能しません。実行するとシェルが空の行を出力しますdir=`cd ".."` && echo $dir
Michael

0

これは、他のすべてからのチェーンソリューションです。たとえば、realpathインストールされていないか、エラーコードで終了したために失敗した場合、パスが正しくなるまで次のソリューションが試行されます。

#!/bin/bash

function getabsolutepath() {
    local target;
    local changedir;
    local basedir;
    local firstattempt;

    target="${1}";
    if [ "$target" == "." ];
    then
        printf "%s" "$(pwd)";

    elif [ "$target" == ".." ];
    then
        printf "%s" "$(dirname "$(pwd)")";

    else
        changedir="$(dirname "${target}")" && basedir="$(basename "${target}")" && firstattempt="$(cd "${changedir}" && pwd)" && printf "%s/%s" "${firstattempt}" "${basedir}" && return 0;
        firstattempt="$(readlink -f "${target}")" && printf "%s" "${firstattempt}" && return 0;
        firstattempt="$(realpath "${target}")" && printf "%s" "${firstattempt}" && return 0;

        # If everything fails... TRHOW PYTHON ON IT!!!
        local fullpath;
        local pythoninterpreter;
        local pythonexecutables;
        local pythonlocations;

        pythoninterpreter="python";
        declare -a pythonlocations=("/usr/bin" "/bin");
        declare -a pythonexecutables=("python" "python2" "python3");

        for path in "${pythonlocations[@]}";
        do
            for executable in "${pythonexecutables[@]}";
            do
                fullpath="${path}/${executable}";

                if [[ -f "${fullpath}" ]];
                then
                    # printf "Found ${fullpath}\\n";
                    pythoninterpreter="${fullpath}";
                    break;
                fi;
            done;

            if [[ "${pythoninterpreter}" != "python" ]];
            then
                # printf "Breaking... ${pythoninterpreter}\\n"
                break;
            fi;
        done;

        firstattempt="$(${pythoninterpreter} -c "import os, sys; print( os.path.abspath( sys.argv[1] ) );" "${target}")" && printf "%s" "${firstattempt}" && return 0;
        # printf "Error: Could not determine the absolute path!\\n";
        return 1;
    fi
}

printf "\\nResults:\\n%s\\nExit: %s\\n" "$(getabsolutepath "./asdfasdf/ asdfasdf")" "${?}"

0

相対パス$ lineにbash文字列置換を使用できます。

line=$(echo ${line/#..\//`cd ..; pwd`\/})
line=$(echo ${line/#.\//`pwd`\/})
echo $line

基本的な文字列前置置換は、
$ {string /#substring / replacement}の公式に従います
これは、ここで詳しく説明されています。https//www.tldp.org/LDP/abs/html/string-manipulation.html

\文字は否定し/、我々はそれが我々が検索/置換する文字列の一部になりたいとき。


0

Mac OS Catalina、Ubuntu 16、Centos 7の間で適切に移植できるソリューションを見つけることができなかったので、Pythonインラインで実行することを決定し、私のbashスクリプトでうまく機能しました。

to_abs_path() {
  python -c "import os; print os.path.abspath('$1')"
}

to_abs_path "/some_path/../secrets"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.