Mengubah jalur relatif ke jalur absolut tanpa tautan simbolis

63

Apakah ada perintah Unix untuk mendapatkan jalur absolut (dan dikanonik) dari jalur relatif yang mungkin mengandung tautan simbolik?

Benjamin
sumber

Jawaban:

80

Anda dapat menggunakan readlinkutilitas, dengan -fopsi:

-f, --canonicalize

      canonicalize  by  following  every symlink in every component of
      the given name recursively; all  but  the  last  component  must
      exist

Beberapa distribusi, misalnya yang menggunakan GNU coreutils dan FreeBSD , juga dilengkapi dengan realpath(1)utilitas yang pada dasarnya hanya memanggil realpath(3)dan melakukan hal yang hampir sama.

Tikar
sumber
17
The -fswitch tidak ada pada Mac OS X (setidaknya dari 10.8.5). Tanpa -fsakelar, ia hanya mengembalikan kode kesalahan.
iconoclast
4
Beberapa sistem datang dengan baik readlinkatau realpath- Solaris dan HP-UX, pada khususnya.
Sildoreth
Untuk masalah Mac: brew install coreutils apple.stackexchange.com/a/69332/23040 , dan jika Anda tidak ingin melakukan langkah yang mengalihkan semua alat standar CLI Mac ke setara GNU yang baru diinstal, cukup hubungi greadlink.
Adrian
19

Mudah dibawa, PWDvariabel diatur oleh shell ke satu lokasi absolut dari direktori saat ini. Komponen apa pun dari jalur itu dapat berupa tautan simbolis.

case $f in
  /*) absolute=$f;;
  *) absolute=$PWD/$f;;
esac

Jika Anda ingin menghilangkan .dan ..juga, ubah ke direktori yang berisi file dan dapatkan di $PWDsana:

if [ -d "$f" ]; then f=$f/.; fi
absolute=$(cd "$(dirname -- "$f")"; printf %s. "$PWD")
absolute=${absolute%?}
absolute=$absolute/${f##*/}

Tidak ada cara portabel untuk mengikuti tautan simbolik. Jika Anda memiliki jalur ke direktori, maka pada sebagian besar unices $(cd -- "$dir" && pwd -P 2>/dev/null || pwd)menyediakan jalur yang tidak menggunakan tautan simbolik, karena shell yang melacak tautan simbolik cenderung diimplementasikan pwd -P("P" untuk "fisik").

Beberapa perangkat menyediakan utilitas untuk mencetak jalur "fisik" ke file.

  • Sistem Linux yang cukup baru (dengan GNU coreutils atau BusyBox) memiliki readlink -f, seperti halnya FreeBSD ≥8.3, NetBSD ≥4.0, dan OpenBSD sejauh 2.2.
  • FreeBSD ≥4.3 telah realpath(juga ada pada beberapa sistem Linux, dan itu ada di BusyBox).
  • Jika Perl tersedia, Anda dapat menggunakan Cwdmodul .

    perl -MCwd -e 'print Cwd::realpath($ARGV[0])' path/to/file
Gilles 'SANGAT berhenti menjadi jahat'
sumber
Mengapa Anda menyalurkan sesuatu ke pwd( $(cd -- "$dir" && pwd -P 2>/dev/null | pwd))? Sepertinya tidak peduli dengan stdin.
Mateusz Piotrowski
1
@MateuszPiotrowski Typo, saya bermaksud menulis||
Gilles 'SO- stop being evil'
5

Apakah pwdsesuai dengan kebutuhan Anda? Ini memberikan jalur absolut dari direktori saat ini. Atau mungkin yang Anda inginkan adalah realpath () .

xanpeng
sumber
3

alisterdan rss67dalam artikel ini memperkenalkan cara yang paling stabil, kompatibel dan termudah. Saya tidak pernah melihat cara yang lebih baik dari ini sebelumnya.

RELPATH=./../
cd $RELPATH
ABSPATH=`pwd`

Jika Anda ingin kembali ke lokasi semula,

ORGPATH=`pwd`
RELPATH=./../
cd $RELPATH
ABSPATH=`pwd`
cd $ORGPATH

atau

RELPATH=./../
cd $RELPATH
ABSPATH=`pwd`
cd -

Saya harap ini membantu. Ini solusi terbaik bagi saya.

Eonil
sumber
12
Itu sebenarnya bukan cara yang baik. Mengganti direktori dan kembali tidak dapat diandalkan: Anda mungkin tidak memiliki izin untuk kembali, atau direktori tersebut mungkin telah diganti namanya untuk sementara. Sebaliknya, mengubah direktori di subkulit: ABSPATH=$(cd -- "$RELPATH" && pwd). Perhatikan tanda kutip ganda di sekitar substitusi (agar tidak pecah jika path berisi karakter spasi dan globbing) dan --(jika path dimulai dengan -). Ini juga hanya berfungsi untuk direktori, dan tidak mengkanoniskan tautan simbolik seperti yang diminta. Lihat jawaban saya untuk lebih jelasnya.
Gilles 'SO- stop being evil'
"> Anda mungkin tidak memiliki izin untuk kembali" Maksud Anda cd ke dir tetapi tidak bisa cd kembali? Bisakah Anda memberikan beberapa contoh skenario.
Talespin_Kit
0

Jawaban lain di sini baik-baik saja tetapi tidak cukup untuk kebutuhan saya. Saya membutuhkan solusi yang dapat saya gunakan dalam skrip saya di mesin apa pun. Solusi saya adalah menulis skrip shell yang saya dapat meminta dari skrip di mana saya membutuhkannya.

#!/bin/sh
if [ $# -eq 0 ] || [ $# -gt 2 ]; then
  printf 'Usage: respath path [working-directory]\n' >&2
  exit 1
fi
cantGoUp=

path=$1
if [ $# -gt 1 ]; then
  cd "$2"
fi
cwd=`pwd -P` #Use -P option to account for directories that are actually symlinks

#Handle non-relative paths, which don't need resolution
if echo "$path" | grep '^/' > /dev/null ; then
  printf '%s\n' "$path"
  exit 0
fi

#Resolve for each occurrence of ".." at the beginning of the given path.
#For performance, don't worry about ".." in the middle of the path.
while true
do
  case "$path" in
    ..*)
      if [ "$cwd" = '/' ]; then
        printf 'Invalid relative path\n' >&2
        exit 1
      fi
      if [ "$path" = '..' ]; then
        path=
      else
        path=`echo "$path" | sed 's;^\.\./;;'`
      fi
      cwd=`dirname $cwd`
      ;;
    *)
      break
      ;;
  esac
done

cwd=`echo "$cwd" | sed 's;/$;;'`
if [ -z "$path" ]; then
  if [ -z "$cwd" ]; then
    cwd='/'
  fi
  printf '%s\n' "$cwd"
else
  printf '%s/%s\n' "$cwd" "$path"
fi

Solusi ini ditulis untuk Bourne Shell untuk portabilitas. Saya memiliki ini dalam skrip bernama "respath.sh".

Script ini dapat digunakan seperti ini:

respath.sh '/path/to/file'

Atau seperti ini

respath.sh '/relative/path/here' '/path/to/resolve/relative/to'

Script menyelesaikan jalur dalam argumen pertama menggunakan jalur dari argumen kedua sebagai titik awal. Jika hanya argumen pertama yang disediakan, maka skrip menyelesaikan jalur relatif ke direktori kerja Anda saat ini.

Sildoreth
sumber
Perhatikan bahwa Anda mungkin hanya akan menemukan cangkang Bourne di Solaris 10 dan sebelumnya saat ini. Itu Bourne shell seperti kebanyakan implementasi shell Bourne sejak SVR2 (1984) telah pwdbuiltin dan tidak mendukung -P(Bourne shell tidak menerapkan bahwa $ PWD logis penanganan seperti kerang POSIX lakukan).
Stéphane Chazelas
Jika Anda menjatuhkan kompatibilitas Bourne, maka Anda dapat menggunakan sintaks POSIX $ {var ## pola} dan menghindari gema | sed yang akan rusak dengan beberapa nilai.
Stéphane Chazelas
Anda lupa memeriksa status keluar dari cd (dan pwd). Anda mungkin harus menggunakan cd -P --juga kalau-kalau argumen 1 berisi beberapa ..
Stéphane Chazelas
caseCek Anda seharusnya ..|../*)bukan ..*.
Stéphane Chazelas
Ada satu kasus kutipan yang hilang didirname $cwd
Stéphane Chazelas
0

Dalam hal, tambahan, Anda memiliki beberapa jalur yang telah ditentukan untuk $1(biasanya ${PWD}), berikut ini akan berfungsi:

CWD="${1:-${PredefinedAbsolutePath}}"
if [ "${CWD}" != "${PredefinedAbsolutePath}}" ]; then
    case $1 in
        /*) echo "CWD=${CWD}"; ;;
        *) CWD=$(realpath $1); echo "CWD=${CWD}"; ;;
    esac
fi
Nikhil
sumber
0

Menghormati orang lain menjawab, saya menemukan fungsi di bawah ini menjadi lebih mudah dan portabel antara linux / MAC OSX daripada yang lain yang saya temukan di mana-mana. Mungkin bisa membantu seseorang.

#!/usr/bin/bash
get_absolute_path(){
    file_path=`pwd $1`
    echo "$file_path/$1"
}
#to assign to a variable
absolute_path=$(get_absolute_path "file.txt")
echo $absolute_path
lauksas
sumber
-1

Anggap variabel a menyimpan nama jalur (a = "apa pun")

1. Untuk jalur yang mengandung ruang potensial:

c=$(grep -o " " <<< $a | wc -l); if (( $c > "0" )); then a=$(sed 's/ /\\ /g' <<< $a); fi

2. Untuk pertanyaan aktual, yaitu mengonversi jalur ke absolut: readlink dapat mengambil konten symlink, yang dapat digunakan sebagai berikut. (note readlink -fakan sempurna tetapi tidak tersedia di setidaknya Mac OS X bash, di mana -f singkatan FORMATS .)

if [[ $(readlink $a) == "" ]]; then a=$(cd $a; pwd); else a=$(cd $(readlink $a); pwd); fi

3. Baru dipelajari pwd -Pdi man pwd: -P adalah untuk " Menampilkan direktori kerja saat ini fisik (semua tautan simbolik diselesaikan) " dan karenanya

if (( $(grep -o " " <<< $a | wc -l) > "0" )); then a=$(sed 's/ /\\ /g' <<< $a); fi; a=$(cd $a; pwd -P)

akan bekerja juga.

Kesimpulan: gabungkan 1 dengan 2 untuk memenuhi kedua kebutuhan Anda, sementara nama jalur yang mengandung ruang sudah diurus dalam yang lebih ringkas 3 .

vxtal
sumber
Anda salah. Misalnya jika jalur Anda menyertakan sebagian besar ruang di atas tidak akan berfungsi.
Anthon
atau kadang-kadang gumpalan char. Dan itu tidak menyelesaikan symlink, seperti yang diminta.
dave_thompson_085
Saya telah menghapus respons "komentar" tentang nama jalur yang berisi spasi dan memodifikasi bagian "jawaban".
vxtal