Apakah ada perintah untuk mengambil jalur absolut yang diberikan jalur relatif?
Sebagai contoh saya ingin $ line berisi path absolut dari setiap file dalam dir ./etc/
find ./ -type f | while read line; do
echo $line
done
Apakah ada perintah untuk mengambil jalur absolut yang diberikan jalur relatif?
Sebagai contoh saya ingin $ line berisi path absolut dari setiap file dalam dir ./etc/
find ./ -type f | while read line; do
echo $line
done
Jawaban:
menggunakan:
find "$(pwd)"/ -type f
untuk mendapatkan semua file atau
echo "$(pwd)/$line"
untuk menampilkan jalur lengkap (jika jalur relatif penting)
$(pwd)
sebagai pengganti $PWD
? (ya, saya tahu pwd
adalah builtin)
Coba realpath
.
~ $ sudo apt-get install realpath # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc
Untuk menghindari perluasan symlink, gunakan realpath -s
.
Jawabannya datang dari " perintah bash / fish untuk mencetak path absolut ke file ".
realpath
sepertinya tidak tersedia di Mac (OS X 10.11 "El Capitan"). :-(
realpath
tampaknya tidak tersedia di CentOS 6 baik
brew install coreutils
akan membawarealpath
realpath
sudah ada. Saya tidak perlu menginstalnya secara terpisah.
realpath
tersedia di Git untuk Windows (bagi saya, setidaknya).
Jika Anda telah menginstal paket coreutils, Anda biasanya dapat menggunakan readlink -f relative_file_name
untuk mengambil yang absolut (dengan semua symlinks diselesaikan)
brew install coreutils
. Namun eksekusi dieksekusi oleh ag:greadlink -f relative_file_name
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
UPD Beberapa penjelasan
"$1"
dirname "$1"
cd "$(dirname "$1")
menuju dir relatif ini dan mendapatkan path absolut untuk itu dengan menjalankan pwd
perintah shell$(basename "$1")
echo
iturealpath
atau apa yang readlink -f
dilakukan. Misalnya, itu tidak berfungsi di jalur di mana komponen terakhir adalah symlink.
-P
opsi untuk pwd
perintah:echo "$(cd "$(dirname "$1")"; pwd -P)/$(basename "$1")"
Untuk apa nilainya, saya memilih jawaban yang dipilih, tetapi ingin berbagi solusi. The downside adalah, itu hanya Linux - Saya menghabiskan sekitar 5 menit mencoba untuk menemukan setara OSX sebelum datang ke Stack overflow. Saya yakin itu di luar sana.
Di Linux Anda dapat menggunakannya readlink -e
bersama-sama dirname
.
$(dirname $(readlink -e ../../../../etc/passwd))
hasil panen
/etc/
Dan kemudian Anda menggunakan dirname
saudara perempuan, basename
untuk mendapatkan nama file
$(basename ../../../../../passwd)
hasil panen
passwd
Menyatukan semuanya ..
F=../../../../../etc/passwd
echo "$(dirname $(readlink -e $F))/$(basename $F)"
hasil panen
/etc/passwd
Anda aman jika Anda menargetkan direktori, tidak basename
akan mengembalikan apa pun dan Anda hanya akan berakhir dengan garis miring ganda pada hasil akhir.
dirname
, readlink
, dan basename
. Itu membantu saya untuk mendapatkan jalur absolut dari tautan simbolik - bukan targetnya.
/home/GKFX
dan saya mengetik touch newfile
, maka sebelum saya menekan enter Anda bisa mengetahui maksud saya "create / home / GKFX / newfile", yang merupakan jalur absolut ke file yang belum ada.
Saya pikir ini yang paling portabel:
abspath() {
cd "$(dirname "$1")"
printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
cd "$OLDPWD"
}
Ini akan gagal jika jalurnya tidak ada.
dirname
adalah utilitas inti GNU, tidak umum untuk semua yang belum saya percaya.
dirname
adalah utilitas standar POSIX, lihat di sini: pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html
${1##*/}
untuk sehari sekarang, dan sekarang saya mengganti sampah dengan basename "$1"
itu tampaknya akhirnya benar-benar menangani jalur yang berakhiran /.
../..
realpath
mungkin yang terbaik
Tapi ...
Pertanyaan awal sangat membingungkan untuk memulai, dengan contoh yang buruk terkait dengan pertanyaan seperti yang dinyatakan.
Jawaban yang dipilih sebenarnya menjawab contoh yang diberikan, dan tidak sama sekali pertanyaan dalam judul. Perintah pertama adalah jawaban itu (benarkah? Aku ragu), dan bisa melakukannya tanpa '/'. Dan saya gagal melihat apa yang dilakukan perintah kedua.
Beberapa masalah beragam:
mengubah pathname relatif menjadi absolut, apa pun yang ditunjukkan, mungkin tidak ada. ( Biasanya, jika Anda mengeluarkan perintah seperti itu touch foo/bar
, nama path foo/bar
harus ada untuk Anda, dan mungkin digunakan dalam perhitungan, sebelum file tersebut benar-benar dibuat. )
mungkin ada beberapa pathname absolut yang menunjukkan file yang sama (atau file potensial), terutama karena tautan simbolik (symlink) di path, tetapi mungkin karena alasan lain (perangkat mungkin dipasang dua kali lebih sebagai hanya baca). Seseorang mungkin atau mungkin tidak ingin menyelesaikan expllink symlinks tersebut.
sampai ke ujung rantai tautan simbolik ke file atau nama yang tidak memiliki tautan. Ini mungkin atau mungkin tidak menghasilkan nama jalur absolut, tergantung pada bagaimana hal itu dilakukan. Dan seseorang mungkin, atau mungkin tidak ingin menyelesaikannya menjadi pathname absolut.
Perintah readlink foo
tanpa opsi memberikan jawaban hanya jika argumennya foo
adalah tautan simbolik, dan jawaban itu adalah nilai symlink itu. Tidak ada tautan lain yang diikuti. Jawabannya mungkin jalur relatif: berapapun nilai argumen symlink.
Namun, readlink
memiliki opsi (-f -e atau -m) yang akan bekerja untuk semua file, dan memberikan satu pathname absolut (yang tanpa symlink) ke file yang sebenarnya dilambangkan oleh argumen.
Ini berfungsi dengan baik untuk apa pun yang bukan symlink, meskipun orang mungkin ingin menggunakan pathname absolut tanpa menyelesaikan symlink menengah di path. Ini dilakukan oleh perintahrealpath -s foo
Dalam kasus argumen symlink, readlink
dengan opsinya lagi akan menyelesaikan semua symlink pada path absolut ke argumen, tetapi itu juga akan mencakup semua symlink yang mungkin ditemui dengan mengikuti nilai argumen. Anda mungkin tidak ingin itu jika Anda menginginkan jalur absolut ke argumen symlink itu sendiri, daripada ke apa pun yang ditautkan. Sekali lagi, jika foo
merupakan symlink, realpath -s foo
akan mendapatkan jalur absolut tanpa menyelesaikan symlink, termasuk yang diberikan sebagai argumen.
Tanpa -s
opsi, realpath
melakukan hampir sama
readlink
, kecuali hanya membaca nilai tautan, serta beberapa hal lainnya. Tidak jelas bagi saya mengapa readlink
memiliki pilihan, menciptakan redundansi yang tampaknya tidak diinginkan dengan
realpath
.
Menjelajahi web tidak banyak bicara, kecuali bahwa mungkin ada beberapa variasi di seluruh sistem.
Kesimpulan: realpath
adalah perintah terbaik untuk digunakan, dengan fleksibilitas terbanyak, setidaknya untuk penggunaan yang diminta di sini.
Solusi favorit saya adalah yang oleh @EugenKonkov karena tidak menyiratkan keberadaan utilitas lain (paket coreutils).
Tetapi gagal untuk jalur relatif "." dan "..", jadi di sini adalah versi yang sedikit ditingkatkan menangani kasus-kasus khusus ini.
Masih gagal jika pengguna tidak memiliki izin untuk cd
masuk ke direktori induk dari jalur relatif.
#! /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
}
Jawaban Eugen tidak berhasil bagi saya, tetapi ini berhasil:
absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"
Catatan, direktori kerja Anda saat ini tidak terpengaruh.
Solusi terbaik, imho, adalah yang diposting di sini: https://stackoverflow.com/a/3373298/9724628 .
Memang membutuhkan python untuk bekerja, tetapi tampaknya mencakup semua atau sebagian besar kasus tepi dan menjadi solusi yang sangat portabel.
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
Jika Anda menggunakan bash pada Mac OS X yang tidak memiliki realpath atau readlink -nya tidak dapat mencetak path absolut, Anda mungkin punya pilihan selain mengkode versi Anda sendiri untuk mencetaknya. Inilah implementasi saya:
(pesta murni)
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[*]}"
)
}
(gunakan 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
}
(aws murni bsd)
#!/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)
}
contoh:
$ abspath I/am/.//..//the/./god/../of///.././war
/Users/leon/I/the/war
Mirip dengan jawaban @ ernest-a tetapi tanpa memengaruhi $OLDPWD
atau mendefinisikan fungsi baru Anda dapat memecat sebuah subkulit(cd <path>; pwd)
$ pwd
/etc/apache2
$ cd ../cups
$ cd -
/etc/apache2
$ (cd ~/..; pwd)
/Users
$ cd -
/etc/cups
Jika jalur relatif adalah jalur direktori, maka coba milikku, harus menjadi yang terbaik:
absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)
echo $absPath
echo "mydir/doc/ mydir/usoe ./mydir/usm" | awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'
Peningkatan ke versi @ ernest-a yang agak bagus:
absolute_path() {
cd "$(dirname "$1")"
case $(basename $1) in
..) echo "$(dirname $(pwd))";;
.) echo "$(pwd)";;
*) echo "$(pwd)/$(basename $1)";;
esac
}
Ini berurusan dengan benar dengan kasus di mana elemen terakhir dari path ..
, dalam hal ini jawaban "$(pwd)/$(basename "$1")"
in @ ernest-a akan muncul sebagai accurate_sub_path/spurious_subdirectory/..
.
Jika Anda ingin mengubah variabel yang berisi jalur relatif menjadi jalur absolut, ini berfungsi:
dir=`cd "$dir"`
"cd" menggema tanpa mengubah direktori kerja, karena dieksekusi di sini dalam sub-shell.
dir=`cd ".."` && echo $dir
Ini adalah solusi berantai dari semua yang lain, misalnya, ketika realpath
gagal, baik karena tidak diinstal atau karena keluar dengan kode kesalahan, maka, solusi berikutnya dicoba sampai mendapatkan jalur yang benar.
#!/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")" "${?}"
Anda bisa menggunakan substitusi string bash untuk jalur $ jalur relatif apa pun:
line=$(echo ${line/#..\//`cd ..; pwd`\/})
line=$(echo ${line/#.\//`pwd`\/})
echo $line
Substitusi front-of-string dasar mengikuti rumus
$ {string / # substring / replacement}
yang dibahas dengan baik di sini: https://www.tldp.org/LDP/abs/html/string-manipulation.html
The \
karakter meniadakan /
ketika kita ingin menjadi bagian dari string yang kita temukan / ganti.
Saya tidak dapat menemukan solusi yang portabel rapi antara Mac OS Catalina, Ubuntu 16 dan Centos 7, jadi saya memutuskan untuk melakukannya dengan python inline dan itu bekerja dengan baik untuk skrip bash saya.
to_abs_path() {
python -c "import os; print os.path.abspath('$1')"
}
to_abs_path "/some_path/../secrets"