web-dev-qa-db-ja.com

Bash:相対パスから絶対パスを取得します

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

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

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

つかいます:

find $(pwd)/ -type f

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

echo $(pwd)/$line

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

54
mpapis

realpathを試してください。

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

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

答えは、「 ファイルへの絶対パスを出力するbash/fishコマンド 」から来ています。

140
epere4

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

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

UPD説明

  1. このスクリプトは、引数として相対パスを取得します"$1"
  2. 次に、そのパスのdirname部分を取得します(dirまたはfileをこのスクリプトに渡すことができます):dirname "$1"
  3. 次に、この相対ディレクトリにcd "$(dirname "$1")し、pwd Shellコマンドを実行して、その絶対パスを取得します。
  4. その後、basenameを絶対パスに追加します:$(basename "$1")
  5. 最後のステップとして、echo it
50
Eugen Konkov

それが価値があるものとして、私は選ばれた答えに投票しましたが、解決策を共有したいと思いました。欠点は、Linuxのみだということです。スタックオーバーフローが発生する前に、OSXで同等のものを見つけるのに5分ほど費やしました。きっとそこにあると思います。

Linuxでは、readlink -edirnameと組み合わせて使用​​できます。

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

利回り

/etc/

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

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

利回り

passwd

全部まとめて..

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

利回り

/etc/passwd

ディレクトリをターゲットにしている場合は安全です。basenameは何も返さず、最終出力にダブルスラッシュが表示されます。

31

私はこれが最もポータブルだと思う:

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

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

24
Ernest A

realpathがおそらく最適です

しかし...

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

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

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

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

  • 同じファイル(または潜在的なファイル)を示すいくつかの絶対パス名が存在する可能性があります。特にパス上のシンボリックリンク(シンボリックリンク)が原因である可能性があります。そのようなシンボリックリンクを明示的に解決したい場合としない場合があります。

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

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

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

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

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

-sオプションを指定しない場合、realpathは、リンクの値を読み取るだけでなく、他のいくつかのことを除いて、readlinkとほとんど同じです。 readlinkにオプションがある理由は明らかではありませんが、realpathで明らかに望ましくない冗長性が生じます。

Webを探索することは、システム間でいくつかのバリエーションがあるかもしれないことを除いて、それ以上のことは言いません。

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

16
babou

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

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

補足説明として、現在の作業ディレクトリは影響を受けません。

6
biomiker

私のお気に入りの解決策は、他のユーティリティ(coreutilsパッケージ)の存在を暗示していないため、@ EugenKonkovによる the one でした。

しかし、相対パス「。」では失敗しました。そして「..」なので、これらの特殊なケースを処理するわずかに改善されたバージョンがあります。

ただし、ユーザーが相対パスの親ディレクトリへの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
}
5
hashchange

realpathが存在せず、readlinkが絶対パスを印刷できないMac OS Xでbashを使用している場合、独自のバージョンをコーディングして印刷することもできます。これが私の実装です。

(純粋な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[*]}"
)
}

(use 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
3
Meow

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

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

最良の解決策であるimhoは、ここに投稿されたものです https://stackoverflow.com/a/3373298/9724628

動作するにはpythonが必要ですが、Edgeのケースのすべてまたはほとんどをカバーし、非常に移植性の高いソリューションであるようです。

  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
2
MrSegFaulty
echo "mydir/doc/ mydir/usoe ./mydir/usm" |  awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'
1
Josh Barton

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

$ pwd
/etc/Apache2
$ cd ../cups 
$ cd -
/etc/Apache2
$ (cd ~/..; pwd)
/Users
$ cd -
/etc/cups
1
fakedrake

相対パスがディレクトリパスの場合は、私のものを試してください。

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

echo $absPath
1
Shunfang Lan

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

1
Tobu

これは、他のすべてからの連鎖ソリューションです。たとえば、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
user

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

   dir=`cd "$dir"`

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

0
Eric H.