Bash'ta bir dosya yolunu nasıl normalleştiriyorsunuz?


205

Ben dönüştürmek istediğiniz /foo/bar/..için/foo

Bunu yapan bir bash komutu var mı?


Düzenleme: pratik durumumda, dizin var.


4
Gerçekte olup olmadığı /foo/barveya hatta /foovar olup olmadığı önemli mi yoksa yalnızca yol adı kurallarına göre dize düzenleme yönüyle ilgileniyor musunuz?
Chen Levy

5
@twalberg ... bu biraz çelişkili ...
Camilo Martin

2
O soru sorar tam olarak ne yapar - - dönüşümleri @CamiloMartin hiç yapmacık değil /foo/bar/..için /foo, ve bir kullanarak bashkomutu. Belirtilmeyen başka gereksinimler varsa, belki de olmalılar ...
twalberg

14
@twalberg Çok fazla TDD yapıyorsunuz -_- '
Camilo Martin

Yanıtlar:


195

bir dosya adının bir kısmını yoldan kesmek istiyorsanız, "dirname" ve "basename" arkadaşlarınızdır ve "realpath" de kullanışlıdır.

dirname /foo/bar/baz 
# /foo/bar 
basename /foo/bar/baz
# baz
dirname $( dirname  /foo/bar/baz  ) 
# /foo 
realpath ../foo
# ../foo: No such file or directory
realpath /tmp/../tmp/../tmp
# /tmp

realpath alternatifleri

Eğer realpath sizin kabuk tarafından desteklenmemektedir, deneyebilirsin

readlink -f /path/here/.. 

Ayrıca

readlink -m /path/there/../../ 

İle aynı şekilde çalışır

realpath -s /path/here/../../

çünkü yolun normalleştirilmesi gerekmiyor.


5
OS X çözümüne ihtiyaç duyanlarınız için aşağıdaki Adam Liss'in cevabına göz atın.
Trenton

stackoverflow.com/a/17744637/999943 Bu, çok ilgili bir cevaptır! Bu QA gönderilerinin her ikisine de bir gün içinde rastladım ve onları birbirine bağlamak istedim.
phyatt

realpath'in 2012 yılında coreutils'e eklendiği görülüyor . github.com/coreutils/coreutils/commits/master/src/realpath.c adresindeki dosya geçmişine bakın .
Noah Lavine

1
Her ikisi de realpathve readlinkGNU çekirdek yardımcı programlarından gelir, bu nedenle muhtemelen ikisini de alırsınız veya hiç almazsınız. Düzgün hatırlarsam, Mac'teki readlink sürümü GNU sürümünden biraz farklıdır: - \
Antoine 'hashar' Musso

102

Bunu yapmak için doğrudan bir bash komutu olup olmadığını bilmiyorum, ama genellikle

normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"

ve iyi çalışıyor.


10
Bu, normal bağlantıları normalleştirir ancak geçici bağlantıları çözmez. Bu bir hata veya özellik olabilir. :-)
Adam Liss

4
$ CDPATH tanımlanmışsa da bir sorun vardır; çünkü "cd foo" sadece geçerli dizinde bulunan bir "foo" değil, $ CDPATH'in bir alt dizini olan herhangi bir "foo" dizinine geçecektir. Sanırım böyle bir şey yapmanız gerekiyor: CDPATH = "" cd "$ {dirToNormalize}" && pwd -P.
mjs

7
Tim'in cevabı kesinlikle en basit ve en portatiftir. CDPATH ile başa çıkmak kolaydır: dir = "$ (ayarlanmamış CDPATH && cd" $ dir "&& pwd)"
David Blevins

2
rm -rf $normalDirDirToNormalize yoksa bu çok tehlikeli olabilir ( )!
Frank Meulenaar

4
Evet, muhtemelen &&@ DavidBlevins'in yorumuna göre kullanmak en iyisidir .
Elias Dorneles

55

Deneyin realpath. Aşağıda bütünüyle kaynak bulunmaktadır ve böylelikle kamu malı bağışlanmıştır.

// realpath.c: display the absolute path to a file or directory.
// Adam Liss, August, 2007
// This program is provided "as-is" to the public domain, without express or
// implied warranty, for any non-profit use, provided this notice is maintained.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>   
#include <limits.h>

static char *s_pMyName;
void usage(void);

int main(int argc, char *argv[])
{
    char
        sPath[PATH_MAX];


    s_pMyName = strdup(basename(argv[0]));

    if (argc < 2)
        usage();

    printf("%s\n", realpath(argv[1], sPath));
    return 0;
}    

void usage(void)
{
    fprintf(stderr, "usage: %s PATH\n", s_pMyName);
    exit(1);
}

1
Bu standart bash kurulumunun bir parçası mıdır? Sistemimizde "komut bulunamadı" mesajı alıyorum (RHEL'de bash 3.00.15 (1))
Tim Whitcomb

4
gnu.org/software/coreutils için readlink, ancak realpath bu paketten geliyor: Packages.debian.org/unstable/utils/realpath
Kent Fredric

8
Centos / OSX'te değil, ubuntu /
BSD'de

4
"Bonus: bu bir bash komutu ve her yerde kullanılabilir!" hakkında realpath. Ayrıca benim favorim olur, ancak aracınızı kaynaktan çıkarmak yerine. Her şey ağır siklet ve çoğu zaman sadece istediğiniz readlink -f... BTW, readlinkaynı zamanda bir bash yerleşik değil coreutils, Ubuntu'nun bir parçası .
Tomasz Gandor

4
Bunu herkese açık alana bağışladığınızı söylediniz, ancak başlık ayrıca "kar amacı gütmeyen kullanım için" diyor - gerçekten herkese açık alana bağışlamak istiyor musunuz? Bu, ticari amaçlı kar amacı güden ticari amaçlarla kullanılabileceği anlamına gelir…
me_ve

38

Taşınabilir ve güvenilir bir çözüm, hemen hemen her yere (Darwin dahil) önceden yüklenmiş olan python kullanmaktır. İki seçeneğiniz var:

  1. abspath mutlak bir yol döndürür ancak sembolik bağlantıları çözmez:

    python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file

  2. realpath mutlak bir yol döndürür ve bunu yaparak sembolik bağlantıları çözer ve kurallı bir yol oluşturur:

    python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file

Her durumda, path/to/filegöreceli veya mutlak bir yol olabilir.


7
Teşekkürler, işe yarayan tek kişi buydu. readlink veya realpath, OS X altında mevcut değildir. Python'un çoğu platformda olması gerekir.
sorin

1
Sadece açıklığa kavuşturmak için, readlinkOS X'te mevcuttur, -fseçenekle değil . Taşınabilir çözümler burada tartışılmıştır .
StvnW

4
Gerçekten linkleri takip etmek istemiyorsanız bu tek aklı başında çözüm olduğunu aklıma boggles. Unix yolu benim AYAK.
DannoHung

34

Coreutils paketindeki readlink yardımcı programını kullanın.

MY_PATH=$(readlink -f "$0")

1
BSD -f bayrağına sahip değildir, yani bu en son MacOS Mojave ve diğer birçok sistemde bile başarısız olacaktır. Taşınabilirlik istiyorsanız -f kullanmayı unutun, birçok işletim sistemi etkilenir.
sorin

1
@sorin. Soru Mac ile ilgili değil, Linux ile ilgili.
mattalxndr

15

readlinkmutlak yolu elde etmek için temel standarttır. Ayrıca yollar veya bir yol yoksa boş dizeleri döndürme avantajına sahiptir (bunu yapmak için bayraklar göz önüne alındığında).

Var olan veya olmayan, ancak ebeveynleri olan bir dizinin mutlak yolunu almak için şunu kullanın:

abspath=$(readlink -f $path)

Tüm ebeveynlerle birlikte bulunması gereken bir dizinin mutlak yolunu bulmak için:

abspath=$(readlink -e $path)

Verilen yolu standartlaştırmak ve varsa bağlantıları takip etmek, ancak aksi halde eksik dizinleri yok saymak ve yolu yine de döndürmek için:

abspath=$(readlink -m $path)

Tek dezavantajı, readlink'in bağlantıları takip edeceğidir. Bağlantıları takip etmek istemiyorsanız, bu alternatif kuralı kullanabilirsiniz:

abspath=$(cd ${path%/*} && echo $PWD/${path##*/})

Bu, $ yolunun dizin kısmına yönlendirir ve geçerli dizini $ yolunun dosya kısmı ile birlikte yazdırır. Chdir'e başarısız olursa, boş bir dize ve stderr'de bir hata alırsınız.


7
readlinkvarsa iyi bir seçenektir. OS X sürümü -eveya -fseçeneklerini desteklemez . İlk üç örnekte, $pathdosya adındaki boşlukları veya joker karakterleri işlemek için çift tırnak işareti kullanmalısınız. Parametre genişletme için +1, ancak bunun bir güvenlik açığı var. Eğer pathboştur, bu irade cdana dizininize. Çift tırnaklara ihtiyacınız var. abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
toxalot

Bu sadece bir örnekti. Eğer güvenlik konusunda cehennem alıyorsanız, gerçekten bash veya başka bir kabuk varyantı kullanmamalısınız. Ayrıca, platformlar arası uyumluluk söz konusu olduğunda bash'ın kendi sorunları vardır ve ana sürümler arasında işlev değişikliği ile ilgili sorunlar vardır. OSX, kabuk komut dosyası oluşturma ile ilgili sorunları olan birçok platformdan sadece biridir, BSD'ye dayandığından bahsetmiyoruz bile. Gerçekten çok platformlu olmanız gerektiğinde, POSIX uyumlu olmanız gerekir, bu nedenle parametre genişletmesi gerçekten pencereden dışarı çıkar. Bir süre Solaris veya HP-UX'e göz atın.
Craig

6
Burada herhangi bir suç anlamına gelmez, ancak bunun gibi belirsiz konulara dikkat etmek önemlidir. Ben sadece bu önemsiz soruna hızlı bir cevap istiyorum ve yukarıdaki yorum için olmasaydı herhangi bir / tüm girişleri ile bu kod güvenilir olurdu. Bu bash tartışmalarında OS-X'i desteklemek de önemlidir. Ne yazık ki OS-X'te desteklenmeyen birçok komut var ve birçok forum Bash'i tartışırken bunu kabul ediyor, bu da daha sonra değil, daha önce değil, çok sayıda platformlar arası sorun almaya devam edeceğimiz anlamına geliyor.
Rebs

14

Eski soru, ancak kabuk düzeyinde tam yol adları ile uğraşıyorsanız çok daha basit bir yol var :

   abspath = "$ (cd" $ yol "&& pwd)"

CD bir alt kabukta gerçekleştiğinden, ana komut dosyasını etkilemez.

Kabuk yerleşik komutlarınızın -L ve -P'yi kabul ettiğini varsayan iki varyasyon şunlardır:

   abspath = "$ (cd -P" $ yol "&& pwd -P)" #çözünmüş sembollerle fiziksel yol
   abspath = "$ (cd -L" $ yol "&& pwd -L)" #lojik yol koruma simgeleri

Şahsen, bir nedenden dolayı sembolik bağlantılardan etkilenmedikçe nadiren bu yaklaşıma ihtiyacım var.

FYI: daha sonra geçerli dizini değiştirse bile çalışan bir komut dosyasının başlangıç ​​dizinini elde etmedeki varyasyon.

name0 = "$ (taban adı" $ 0 ")"; #base komut dosyasının adı
dir0 = "$ (cd" $ (dirname "$ 0") "&& pwd)"; #absolute başlangıç ​​dizini

Komut dosyası, cd / pwd olmadan genellikle sadece ./script.sh gibi komutlar tarafından çalıştırılsa bile, her zaman mutlak dizine sahip olmanızı sağlar. Komut dosyası daha sonra bir cd yaparsa işe yaramaz.


9

Adam Liss'in belirttiği gibi realpath, her dağıtımla birlikte verilmemiştir. Bu bir utanç, çünkü en iyi çözüm. Sağlanan kaynak kodu harika ve muhtemelen şimdi kullanmaya başlayacağım. Şimdiye kadar kullandığım şey, burada sadece tamlık için paylaşıyorum:

get_abs_path() {
     local PARENT_DIR=$(dirname "$1")
     cd "$PARENT_DIR"
     local ABS_PATH="$(pwd)"/"$(basename "$1")"
     cd - >/dev/null
     echo "$ABS_PATH"
} 

Sembolik bağlantıları çözmesini istiyorsanız, pwdile değiştirin pwd -P.


pwd -PBu durum için bir seçenek var ... $(basename "$1")Başka bir dizindeki bir dosyaya bir sembolik bağlantı olsaydı ne olacağını düşünün . pwd -PSadece basename kısmını yolunun dizin kısmında sembolik giderir, ama.
Mart'ta toxalot

7

Son çözümüm şuydu:

pushd foo/bar/..
dir=`pwd`
popd

Tim Whitcomb'un cevabına dayanarak.


Argüman bir dizin değilse bunun başarısız olduğundan şüpheleniyorum. / Usr / bin / java'nın nereye gittiğini bilmek istediğimi varsayalım?
Edward Falk

1
Bunun bir dosya olduğunu biliyorsanız, pushd $(dirname /usr/bin/java)deneyebilirsiniz.
schmunk

5

Tam olarak bir cevap değil, belki de bir takip sorusu (orijinal soru açık değildi):

readlinkaslında sembolik bağlantıları takip etmek istiyorsanız sorun yok. Ama sadece normalleştiren bir kullanım durumu da var ./ve ../ve //tamamen sözdizimsel yapılabilir dizileri, olmadan sembolik standart hale. readlinkbunun için iyi değildir ve ikisi de değildir realpath.

for f in $paths; do (cd $f; pwd); done

varolan yollar için çalışır, ancak diğerleri için keser.

Bir sed(script iteratif dizilerin yerini olamayacağını hariç, iyi bir bahis olacak gibi görünüyor /foo/bar/baz/../..-> /foo/bar/..> - /footüm sistemlerde üstlenme ya da bazı çirkin döngüsü kullanmak çıkışını karşılaştırmak güvenli değildir Perl, gibi bir şey kullanmadan) sediçin girdisi.

Java kullanan tek astarlı FWIW (JDK 6+):

jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths

realpathBir sahiptir -ssembolik bağlantıları ve sadece çözmek başvuruları gidermezse seçeneği /./, /../ve ekstra kaldırmak /karakterleri. -mSeçenekle birleştirildiğinde , realpathyalnızca dosya adında çalışır ve gerçek herhangi bir dosyaya dokunmaz. Bu sesler mükemmel bir çözüm gibi. Ancak ne yazık ki, realpathbirçok sistemde hala eksik.
toxalot

..Sembolik semboller söz konusu olduğunda bileşenleri kaldırmak sözdizimsel olarak yapılamaz. /one/two/../threeaynı şey değildir /one/threeeğer twosembolik köprü ise /foo/bar.
jrw32982 Monica

@ jrw32982 evet cevabımda dediğim gibi bu symlink standartlaştırma istendiğinde veya gerekli olmadığında bir kullanım durumudur .
Jesse Glick

@ JesseGlick sadece sembolik bağlantıları standartlaştırmak isteyip istemediğinize ilişkin bir durum değildir. Algoritmanız aslında yanlış cevap veriyor. Cevabınızın doğru olması için, herhangi bir sembolik bağın bulunmadığını (veya yalnızca belirli bir formda olduklarını) bir önsel bilmeniz gerekir. Cevabınız, onları kurallı hale getirmek istemediğinizi söylüyor , yola herhangi bir sembolik bağ yok.
jrw32982 Monica

Herhangi bir sabit, varolan dizin yapısı varsayılmadan normalleştirmenin yapılması gereken kullanım durumları vardır. URI normalizasyonu benzerdir. Bu durumlarda, sonucun daha sonra uygulanacağı bir dizinin yakınında sembolik bağlantılar olması durumunda sonucun genellikle doğru olmayacağı doğal bir sınırlamadır.
Jesse Glick

5

Partiye geç kaldım, ama böyle bir dizi parçayı okuduktan sonra yaptığım çözüm bu:

resolve_dir() {
        (builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi)
}

Bu, 1 $ mutlak yolunu çözer, ~ ile iyi oynar, sembol bağlantılarını bulundukları yolda tutar ve dizin yığınınızla uğraşmaz. Tam yolu döndürür veya yoksa hiçbir şey döndürmez. 1 $ 'ın bir dizin olmasını bekler ve eğer değilse muhtemelen başarısız olur, ancak bu kendiniz yapmak için kolay bir kontrol.


4

Konuşkan ve biraz geç cevap. Daha eski RHEL4 / 5'e yapıştığım için bir tane yazmam gerekiyor. Mutlak ve göreli bağlantıları işlerim ve //, /./ ve somedir /../ girdilerini basitleştirir.

test -x /usr/bin/readlink || readlink () {
        echo $(/bin/ls -l $1 | /bin/cut -d'>' -f 2)
    }


test -x /usr/bin/realpath || realpath () {
    local PATH=/bin:/usr/bin
    local inputpath=$1
    local changemade=1
    while [ $changemade -ne 0 ]
    do
        changemade=0
        local realpath=""
        local token=
        for token in ${inputpath//\// }
        do 
            case $token in
            ""|".") # noop
                ;;
            "..") # up one directory
                changemade=1
                realpath=$(dirname $realpath)
                ;;
            *)
                if [ -h $realpath/$token ] 
                then
                    changemade=1
                    target=`readlink $realpath/$token`
                    if [ "${target:0:1}" = '/' ]
                    then
                        realpath=$target
                    else
                        realpath="$realpath/$target"
                    fi
                else
                    realpath="$realpath/$token"
                fi
                ;;
            esac
        done
        inputpath=$realpath
    done
    echo $realpath
}

mkdir -p /tmp/bar
(cd /tmp ; ln -s /tmp/bar foo; ln -s ../.././usr /tmp/bar/link2usr)
echo `realpath /tmp/foo`

3

GitHub'a ücretsiz ve ipoteksiz kullanım için yerleştirdiğimiz yeni Bash kütüphanesi ürün realpath-lib'imizi deneyin . Tamamen belgelenmiştir ve harika bir öğrenme aracıdır.

Yerel, göreli ve mutlak yolları çözer ve Bash 4+ dışında herhangi bir bağımlılığı yoktur; bu yüzden hemen hemen her yerde çalışmalıdır. Ücretsiz, temiz, basit ve öğretici.

Yapabilirsin:

get_realpath <absolute|relative|symlink|local file path>

Bu işlev kütüphanenin çekirdeğidir:

function get_realpath() {

if [[ -f "$1" ]]
then 
    # file *must* exist
    if cd "$(echo "${1%/*}")" &>/dev/null
    then 
        # file *may* not be local
        # exception is ./file.ext
        # try 'cd .; cd -;' *works!*
        local tmppwd="$PWD"
        cd - &>/dev/null
    else 
        # file *must* be local
        local tmppwd="$PWD"
    fi
else 
    # file *cannot* exist
    return 1 # failure
fi

# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success

}

Ayrıca get_dirname, get_filename, get_ stemname ve validate_path işlevlerini de içerir. Platformlar arasında deneyin ve geliştirilmesine yardımcı olun.


3

Sorun realpathBSD'de (veya bu konuda OSX'te) bulunmamasıdır. Linux Journal'dan oldukça eski (2009) bir makaleden çıkarılan basit bir tarif :

function normpath() {
  # Remove all /./ sequences.
  local path=${1//\/.\//\/}

  # Remove dir/.. sequences.
  while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
    path=${path/${BASH_REMATCH[0]}/}
  done
  echo $path
}

Bu varyant da gelmez dikkat değil var olma yolunu gerektirir.


Ancak bu sembolik bağlantıları çözmez. Realpath, kökten başlayarak yolları işler ve ilerledikçe sembolik bağlantıları izler. Bütün bunlar üst referansları daraltmak.
Martijn Pieters

2

@ Andre'nin cevabına dayanarak, birinin döngüsüz, tamamen dize manipülasyon tabanlı bir çözümden sonra olması durumunda biraz daha iyi bir sürüme sahip olabilirim. realpathVeya kullanmanın dezavantajı olan herhangi bir sembolik bağlantıdan vazgeçmek istemeyenler için de yararlıdır readlink -f.

3.2.25 ve üstü bash sürümlerinde çalışır.

shopt -s extglob

normalise_path() {
    local path="$1"
    # get rid of /../ example: /one/../two to /two
    path="${path//\/*([!\/])\/\.\./}"
    # get rid of /./ and //* example: /one/.///two to /one/two
    path="${path//@(\/\.\/|\/+(\/))//}"
    # remove the last '/.'
    echo "${path%%/.}"
}

$ normalise_path /home/codemedic/../codemedic////.config
/home/codemedic/.config

Bu güzel bir fikir, ama bash'ın çeşitli farklı sürümlerinde çalışmasını sağlamaya çalışırken 20 dakika harcadım. Bunun çalışması için extglob kabuk seçeneğinin açık olması gerektiği ortaya çıkıyor ve varsayılan olarak değil. Bash işlevselliği söz konusu olduğunda, bu ayrıntılar işletim sistemleri arasında değişiklik gösterebileceğinden hem gerekli sürümü hem de varsayılan olmayan seçenekleri belirtmek önemlidir. Örneğin, Mac OSX'in (Yosemite) yeni bir sürümü yalnızca güncel olmayan bir bash (3.2) sürümü ile birlikte gelir.
drwatsoncode

Üzgünüm @ricovox; Şimdi bunları güncelledim. Orada sahip olduğunuz Bash'in tam sürümünü bilmek için sabırsızlanıyorum. Yukarıdaki formül (güncellenmiş) bash 3.2.25 ile gelen CentOS 5.8 üzerinde çalışır
ϹοδεMεδιϲ

Karışıklık için özür dilerim. Bu kod, extglob'u açtıktan sonra Mac OSX bash (3.2.57) sürümümde çalıştı. Bash sürümleriyle ilgili notum daha genel bir nottu (bu aslında bash'daki regex ile ilgili başka bir cevap için daha fazla geçerlidir).
drwatsoncode

2
Yine de cevabınızı takdir ediyorum. Kendi başıma bir üs olarak kullandım. Bu arada, senin başarısız olduğu birkaç durum fark ettim: (1) Göreceli yollar hello/../world(2) Dosya adındaki noktalar /hello/..world(3) Çift eğik çizgi /hello//../world(4) Çift eğik çizgi öncesi veya sonrası nokta /hello//./worldveya /hello/.//world (5) Akım sonrası ebeveyn: /hello/./../world/ (6) Ebeveyn: Ebeveyn:, /hello/../../worldvb. sonra - Yolun değişmesi durana kadar düzeltmeler yapmak için bir döngü kullanılarak bunlardan bazıları düzeltilebilir. (Ayrıca kaldırın dir/../, /dir/..ancak dir/..sonuna kadar kaldırın .)
drwatsoncode

0

Loveborg'un mükemmel python snippet'ine dayanarak şunu yazdım:

#!/bin/sh

# Version of readlink that follows links to the end; good for Mac OS X

for file in "$@"; do
  while [ -h "$file" ]; do
    l=`readlink $file`
    case "$l" in
      /*) file="$l";;
      *) file=`dirname "$file"`/"$l"
    esac
  done
  #echo $file
  python -c "import os,sys; print os.path.abspath(sys.argv[1])" "$file"
done

0
FILEPATH="file.txt"
echo $(realpath $(dirname $FILEPATH))/$(basename $FILEPATH)

Bu, dosya olmasa bile çalışır. Dosyayı içeren dizinin var olmasını gerektirir.


GNU Realpath, kullanmadığınız sürece yoldaki son öğenin de bulunmasını gerektirmezrealpath -e
Martijn Pieters

0

Üçünü de yapacak bir çözüme ihtiyacım vardı:

  • Stok Mac üzerinde çalışın. realpathve readlink -feklentiler
  • Sembolik bağlantıları çözme
  • Hata işleme

Cevapların hiçbirinde # 1 ve # 2 yoktu. Başkalarını daha fazla yak tıraş etmek için # 3 ekledim.

#!/bin/bash

P="${1?Specify a file path}"

[ -e "$P" ] || { echo "File does not exist: $P"; exit 1; }

while [ -h "$P" ] ; do
    ls="$(ls -ld "$P")"
    link="$(expr "$ls" : '.*-> \(.*\)$')"
    expr "$link" : '/.*' > /dev/null &&
        P="$link" ||
        P="$(dirname "$P")/$link"
done
echo "$(cd "$(dirname "$P")"; pwd)/$(basename "$P")"

İşte alıntıyı tam olarak uygulamak için yollarda bazı bükülmüş boşluklar içeren kısa bir test örneği

mkdir -p "/tmp/test/ first path "
mkdir -p "/tmp/test/ second path "
echo "hello" > "/tmp/test/ first path / red .txt "
ln -s "/tmp/test/ first path / red .txt " "/tmp/test/ second path / green .txt "

cd  "/tmp/test/ second path "
fullpath " green .txt "
cat " green .txt "

0

Bunun eski bir soru olduğunu biliyorum. Hala bir alternatif öneriyorum. Son zamanlarda aynı sorunla karşılaştım ve bunu yapmak için mevcut ve taşınabilir bir komut bulamadım. Bu yüzden hile yapabilen bir işlev içeren aşağıdaki kabuk komut dosyasını yazdım.

#! /bin/sh                                                                                                                                                

function normalize {
  local rc=0
  local ret

  if [ $# -gt 0 ] ; then
    # invalid
    if [ "x`echo $1 | grep -E '^/\.\.'`" != "x" ] ; then
      echo $1
      return -1
    fi

    # convert to absolute path
    if [ "x`echo $1 | grep -E '^\/'`" == "x" ] ; then
      normalize "`pwd`/$1"
      return $?
    fi

    ret=`echo $1 | sed 's;/\.\($\|/\);/;g' | sed 's;/[^/]*[^/.]\+[^/]*/\.\.\($\|/\);/;g'`
  else
    read line
    normalize "$line"
    return $?
  fi

  if [ "x`echo $ret | grep -E '/\.\.?(/|$)'`" != "x" ] ; then
    ret=`normalize "$ret"`
    rc=$?
  fi

  echo "$ret"
  return $rc
}

https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c


0

Mümkün olan en yüksek performansa (eğlence için) odaklanarak bunu ele almak için yalnızca yerleşik bir işlev yaptım. Sembolik bağlantıları çözmez, bu nedenle temel olarak aynıdır realpath -sm.

## A bash-only mimic of `realpath -sm`. 
## Give it path[s] as argument[s] and it will convert them to clean absolute paths
abspath () { 
  ${*+false} && { >&2 echo $FUNCNAME: missing operand; return 1; };
  local c s p IFS='/';  ## path chunk, absolute path, input path, IFS for splitting paths into chunks
  local -i r=0;         ## return value

  for p in "$@"; do
    case "$p" in        ## Check for leading backslashes, identify relative/absolute path
    '') ((r|=1)); continue;;
    //[!/]*)  >&2 echo "paths =~ ^//[^/]* are impl-defined; not my problem"; ((r|=2)); continue;;
    /*) ;;
    *)  p="$PWD/$p";;   ## Prepend the current directory to form an absolute path
    esac

    s='';
    for c in $p; do     ## Let IFS split the path at '/'s
      case $c in        ### NOTE: IFS is '/'; so no quotes needed here
      ''|.) ;;          ## Skip duplicate '/'s and '/./'s
      ..) s="${s%/*}";; ## Trim the previous addition to the absolute path string
      *)  s+=/$c;;      ### NOTE: No quotes here intentionally. They make no difference, it seems
      esac;
    done;

    echo "${s:-/}";     ## If xpg_echo is set, use `echo -E` or `printf $'%s\n'` instead
  done
  return $r;
}

Not: Bir yolun başlangıcında //tam olarak iki çift eğik çizgi uygulama tanımlı davranış olduğundan, bu işlev ile başlayan yolları işlemez . Ancak /,/// ve böylece gayet iyi.

Bu işlev tüm uç durumları düzgün bir şekilde ele alıyor gibi görünüyor, ancak hala ilgilenmediğim bazı şeyler olabilir.

Performans Notu: binlerce argümanla çağrıldığında, abspathyaklaşık 10 kat daha yavaş çalışır realpath -sm; tek bir argümanla çağrıldığında , çoğunlukla her seferinde yeni bir program yürütmesi gerekmediği için abspathmakinemden> 110 kat daha hızlı çalışır realpath -sm.


-1

Bugün statyolları çözmek için komutu kullanabileceğinizi keşfettim .

"~ / Documents" gibi bir dizin için:

Bunu çalıştırabilirsiniz:

stat -f %N ~/Documents

Tam yolu elde etmek için:

/Users/me/Documents

Sembolik bağlar için% Y biçimi seçeneğini kullanabilirsiniz:

stat -f %Y example_symlink

Hangi gibi bir sonuç döndürebilir:

/usr/local/sbin/example_symlink

Biçimlendirme seçenekleri * NIX'in diğer sürümlerinde farklı olabilir, ancak bunlar benim için OSX'te çalıştı.


1
stat -f %N ~/Documentsçizgi Kabuk değiştiriyor ... kırmızı ringa ~/Documentsile /Users/me/Documentsve statsadece kelimesi kelimesine onun argüman yazdırıyor.
danwyand

-4

Kullanarak basit bir çözüm node.js:

#!/usr/bin/env node
process.stdout.write(require('path').resolve(process.argv[2]));
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.