Bash'de olası bir göreli yolu genişlet


82

Komut dosyamın argümanları olarak bazı dosya yolları var. Elbette bunlar göreceli olabilir (veya ~ içerebilir). Ancak yazdığım işlevler için mutlak, ancak sembolik bağları çözülmemiş yollara ihtiyacım var.

Bunun için herhangi bir işlev var mı?



7
readlinksembolik bağları çözer; OP bunu istemiyor.
Keith Thompson



1
echo $(cd some_directory && pwd), symlink çözümleme, bazı_dizinler yoksa başarısız olur. çalışma dizini etkilenmez. veya bir değişkene atayın MY_PATH=$(cd some_directory && pwd).
qeatzy

Yanıtlar:


79

MY_PATH=$(readlink -f $YOUR_ARG)"./"ve gibi göreli yolları çözecek"../"

Bunu da düşünün ( kaynak ):

#!/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

bu kesinlikle güzel, ancak sorun şu ki, simlinkler çözüldü ve bu, kullanıcılarla bazı karışıklıklara yol açacak.
laar

man pwd: "-P Fiziksel mevcut çalışma dizinini görüntüleyin (tüm sembolik bağlantılar çözüldü)", sadece-P
andsens

7
readlink -fOS X'de işlevselliği elde etmek için Homebrew'i kullanabilir ve ardından çalıştırabilirsiniz: $ brew install coreutils Daha sonra kullanabilirsiniz greadlink -f.
jgpawletko

46

http://www.linuxquestions.org/questions/programming-9/bash-script-return-full-path-and-filename-680368/page3.html aşağıdakilere sahiptir

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
}

Kullanışlı olduğu bir duruma girmek için pushd/ kullanır .popdpwd


2
Ne yazık ki, buradaki en iyi cevap bu ve ait olduğu yere göre oylanmadı. OP'nin sorusunu doğru cevaplamak için +1. Çözülmemiş sembolik bağların olmasını istiyor, ki bu gerçekten bash'ın yerel sorunu DIRSTACKve başka bir şey değil. Güzel çözüm!
cptstubing06

5
Standart shuyumluluğu hedeflerken, pushd; cd <dir>; <cmd>; popdile değiştirilebilir (cd <dir>; <cmd>).
Abbafei

16

Basit tek astar:

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

Kullanım:

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

Hala yolun var olup olmadığından tamamen habersiz olmasını nasıl sağlayabileceğimi anlamaya çalışıyorum (bu nedenle dosya oluştururken de kullanılabilir).


1
Bu, mevcut çalışma dizinini değiştirmeyecek mi? Daha cd -sonra sıfırlamam gerekir mi?
devios1

4
Gerek yok. Parenlere dikkat edin, içerideki komutların bir alt kabukta çalıştırılmasına neden olurlar. Bir alt kabuğun durumu (örneğin, çalışma dizini) üst kabuğuna sızmaz. Daha fazlasını buradan okuyun: tldp.org/LDP/abs/html/subshells.html
andsens

1
Güzel tek astarlı. Yine de bir sorun var: eğer $ 1'in genişletilmiş değeri hiç bölü işaretine sahip değilse (yani, mevcut dizindeki bir dosya adı ise), $ {1% / *} dosya adına genişler ve cd komutu başarısız olur. Bunun yerine $ (dizin adı $ 1) kullanmak isteyebilirsiniz, bu '.' bu durumda.
Paulo

Haklısın, güncellendi. Bununla ${1##*/}birlikte basename, ayrı bir değişkene koymadan, boşluklu yollar düzgün çalışmıyor.
andsens

1
@BryanP Aslında sürdürdüğüm dotfile senkronizörü için tamamen denize düştüm. Gereksiz yol parçalarını kaldıran bir clean_path () işlevi vardır, sonuna göreceli bir yol yapıştırırsanız $PWDve oradan geçerseniz,
andsens

8

Bu benim için OS X'te hile yapıyor: $(cd SOME_DIRECTORY 2> /dev/null && pwd -P)

Her yerde çalışmalı. Diğer çözümler çok karmaşık görünüyordu.


4

OS X'te kullanabilirsiniz

stat -f "%N" YOUR_PATH

linux üzerinde realpathçalıştırılabilir dosyanız olabilir . değilse, aşağıdakiler işe yarayabilir (yalnızca bağlantılar için değil):

readlink -c YOUR_PATH

20
OS X yanıtı çalışmıyor. stat -f "%N" PATHbana tam olarak verdiğim yolu veriyor.
Lily Ballard

3

readlink -f <relative-path>Örneğin kullanın

export FULLPATH=`readlink -f ./`

2

Belki bu daha okunabilirdir ve bir alt kabuk kullanmaz ve mevcut dizini değiştirmez:

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

Başka bir yöntem var. Göreli bir yolu çözmek için bash betiğine python katıştırmayı kullanabilirsiniz.

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

0

kendi kendine düzenleme, OP'nin çözülmüş sembolik bağlantıları aramadığını söylediğini fark ettim:

"Ancak yazdığım işlevler için mutlak, ancak sembolik bağları çözülmemiş yollara ihtiyacım var."

Öyleyse tahmin et bu onun sorusuna pek de uygun değil. :)

Yıllar boyunca bununla birçok kez karşılaştığım için ve bu sefer OSX ve linux üzerinde kullanabileceğim saf bir bash taşınabilir versiyona ihtiyacım vardı, devam ettim ve bir tane yazdım:

Canlı versiyon burada yaşıyor:

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

ancak SO adına, işte mevcut sürüm (iyi test edildiğini düşünüyorum ... ancak geri bildirime açığım!)

Plain bourne shell (sh) için çalışmasını sağlamak zor olmayabilir, ama denemedim ... $ FUNCNAME'i çok seviyorum. :)

#!/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
}

İşte demleme sayesinde klasik bir örnek:

%% 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

bu işlevi kullanın ve -gerçek- yolunu döndürecektir:

%% 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

İşletim sisteminiz destekliyorsa, şunu kullanın:

realpath "./some/dir"

Ve bir değişkende kullanmak:

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

Yolunuzu genişletecek. Ubuntu ve CentOS üzerinde test edilmiştir, sizde bulunmayabilir. Bazıları readlink'i öneriyor, ancak readlink için dokümantasyon şöyle diyor:

Realpath (1) 'in, standartlaştırma işlevselliği için kullanılması tercih edilen komuttur.

İnsanlar neden değişkenlerimden alıntı yaptığımı merak ederse, yollardaki boşlukları korumaktır. Gibi yapıyor realpath some pathsize iki farklı yol sonuçlar verecektir. Ama realpath "some path"bir tane dönecek. Alıntılanan parametreler ftw :)


-1

Sadece bash kullanmak zorunda mısınız? Bunu yapmam gerekiyordu ve Linux ile OS X arasındaki farklılıklardan bıktım. Bu yüzden hızlı ve kirli bir çözüm için PHP'yi kullandım.

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

Çok zarif bir çözüm olmadığını biliyorum ama işe yarıyor.


Bu, dosyanın yolunu zaten biliyorsanız veya dosya mevcut dizindeyse, dosyanın yolunu döndürmekten başka bir şey yapmaz ... dosyayı PATH ayarlarında bulamaz
G-Man
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.