OS X ile Bash komut dosyası mutlak yolu


99

OS X'te şu anda çalışan komut dosyasının mutlak yolunu elde etmeye çalışıyorum.

Birçok cevabın gittiğini gördüm readlink -f $0. Ancak OS readlinkX'ler BSD'ler ile aynı olduğu için çalışmaz (GNU'nun sürümüyle çalışır).

Buna ezber bozan bir çözüm var mı?





16
$( cd "$(dirname "$0")" ; pwd -P )
Jason S

Yanıtlar:


91

realpath()İşi yapacak bir C işlevi var, ancak komut satırında hiçbir şey göremiyorum. İşte hızlı ve kirli bir değiştirme:

#!/bin/bash

realpath() {
    [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

realpath "$0"

Bu, bir /. Değilse, göreceli bir yol olmalı, bu yüzden öne doğru ilerliyor $PWD. #./Parçası kapalı şeritler ./önünden $1.


1
Ayrıca C işlevini de fark ettim ancak karşılık gelen ikili veya herhangi bir şey bulamadım. Her neyse, fonksiyonun ihtiyaçlarıma tam olarak uyuyor. Teşekkürler!
Ördek Mighty Kauçuk

7
Bunun sembolik bağlantılara başvurmadığını unutmayın.
Adam Vandenberg

17
realpath ../somethingiade$PWD/../something
Lri

Bu benim için çalıştı, ancak "realpath_osx ()" olarak yeniden adlandırdım çünkü bu bash betiğini bir linux kabuğuna göndermem gerekebilir ve "gerçek" realpath ile çarpışmasını istemedim! (Eminim daha zarif bir yol vardır, ama ben bir bash n00b'yim.)
Andrew Theken

8
command -v realpath >/dev/null 2>&1 || realpath() { ... }
Kara Brightwell

116

Bu üç basit adım, bunu ve diğer birçok OS X sorununu çözecektir:

  1. Homebrew'i yükleyin
  2. brew install coreutils
  3. grealpath .

(3) sadece olarak değiştirilebilir realpath, bkz. (2) çıktı


3
Bu, sorunu çözer, ancak istemediğiniz veya ihtiyaç duymayabileceğiniz veya hedef sistemde bulunmayan şeyleri yüklemenizi gerektirir, yani OS X
Jason S

6
@JasonS GNU coreutils çok iyi değil kullanımına. Çok sayıda harika yardımcı program içerir. Aslında o kadar iyi ki Linux çekirdeği onsuz işe yaramaz ve neden bazı insanlar ona GNU / Linux diyor. Coreutils harika. mükemmel cevap, seçilen cevap bu olmalıdır.

7
@omouse Soru özellikle 'kullanıma hazır bir çözümden' bahsediyor (OS X için). Coreutils'in ne kadar harika olduğuna bakılmaksızın, OS X'te 'kutunun dışında' değildir, bu yüzden cevap iyi bir cevap değildir. Temel bilgilerin ne kadar iyi olduğu veya OS X'tekinden daha iyi olup olmadığı sorusu değil. "Kutudan çıkar çıkmaz" çözümler $( cd "$(dirname "$0")" ; pwd -P )benim için iyi çalışıyor.
Jason S

2
OS X'in "özelliklerinden" biri, dizin ve dosya adlarının büyük / küçük harfe duyarlı olmamasıdır. Sonuç olarak bir dizin olarak adlandırılan varsa XXXve bazı biri cd xxxdaha sonra pwddönecektir .../xxx. Bu cevap dışında, yukarıdaki tüm çözümler xxx, gerçekten istediğiniz şey olduğunda geri döner XXX. Teşekkür ederim!
Andrew

1
Kodu sürekli olarak kopyalamanın, yapıştırmanın ve yeniden icat etmenin mevcut bir paket yöneticisi çözümünden daha iyi olduğunu bilmiyorum. İhtiyaç duyarsanız realpath, diğer öğelere neredeyse kesinlikle ihtiyacınız olacaksa ne olur coreutils? Bu işlevleri bash'ta da yeniden yazmak ister misiniz? : P
Ezekiel Victor

28

Ugh. Önceki cevapları birkaç nedenden dolayı biraz isteyerek buldum: özellikle, birden fazla sembolik bağlantı düzeyini çözmezler ve son derece "Bash-y" dir. Orijinal soru açıkça bir "Bash betiği" isterken, aynı zamanda Mac OS X'in BSD benzeri, GNU'sundan da bahsediyor readlink. İşte burada makul bir taşınabilirlik denemesi (bash ile 'sh' ve tire olarak kontrol ettim), keyfi sayıda sembolik bağlantıyı çözme; ve ayrıca yol (lar) da beyaz boşluklarla da çalışmalıdır, ancak beyaz boşluk varsa, yardımcı programın temel adı olan davranıştan emin değilim, bu yüzden belki, um, bundan kaçının?

#!/bin/sh
realpath() {
  OURPWD=$PWD
  cd "$(dirname "$1")"
  LINK=$(readlink "$(basename "$1")")
  while [ "$LINK" ]; do
    cd "$(dirname "$LINK")"
    LINK=$(readlink "$(basename "$1")")
  done
  REALPATH="$PWD/$(basename "$1")"
  cd "$OURPWD"
  echo "$REALPATH"
}
realpath "$@"

Umarım bu birilerinin işine yarayabilir.


1
localGlobal isim alanını kirletmemek için sadece fonksiyon içinde tanımlanan değişkenler için kullanmanızı tavsiye ederim . Örn local OURPWD=.... En azından bash için çalışıyor.
Michael Paesold

Ayrıca kod, özel değişkenler için büyük harf kullanmamalıdır. Büyük harfli değişkenler sistem kullanımı için ayrılmıştır.
üçlü

Senaryo için teşekkürler. Bağlantı (lar) ın ve gerçek dosyanın farklı temel adlara sahip olması durumunda BASENAME=$(basename "$LINK") , while'ın içine bir eklemek ve bunu ikinci LINK ayarlayıcı ve REALPATH ayarlayıcı
stroborobo'da

Bu, sembolik bağları ve ..ana başvuruları tam olarak yapacağı şekilde işlemez realpath. Homebrew ile coreutilsyüklü, deneyin ln -s /var/log /tmp/linkexampleardından realpath /tmp/linkexample/../; bu baskılar /private/var. Ancak işleviniz /tmp/linkexample/..bunun yerine üretir , çünkü ..bir sembolik bağlantı değildir.
Martijn Pieters


7

Bir sistem sağlama betiğinde kullanılmak üzere bir çözüm arıyordum, yani, Homebrew kurulmadan önce çalıştırın. Uygun bir çözüm olmadığından, görevi platformlar arası bir dile aktarırdım, örneğin Perl:

script_abspath=$(perl -e 'use Cwd "abs_path"; print abs_path(@ARGV[0])' -- "$0")

Daha sıklıkla istediğimiz şey, içerdiği dizindir:

here=$(perl -e 'use File::Basename; use Cwd "abs_path"; print dirname(abs_path(@ARGV[0]));' -- "$0")

Harika! Perl küçük ek yük için iyidir! Muhtemelen ilk sürümü basitleştirebilirsiniz FULLPATH=$(perl -e "use Cwd 'abs_path'; print abs_path('$0')"). Karşı herhangi bir sebep var mı?
F Pereira

@FPereira Kullanıcı tarafından sağlanan kaçış karakterli dizeden program kodu oluşturmak asla iyi bir fikir değildir. ''kurşun geçirmez değildir. $0Örneğin tek bir alıntı içeriyorsa kırılır . Çok basit bir örnek: sürümünüzü deneyin ve tam yolu /tmp/'/test.shile arayın /tmp/'/test.sh.
4ae1e1

Ya da daha basit /tmp/'.sh.
4ae1e1

6

Bir olmadığı realpath diğerleri gibi sivri out var:

// realpath.c
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char* argv[])
{
  if (argc > 1) {
    for (int argIter = 1; argIter < argc; ++argIter) {
      char *resolved_path_buffer = NULL;
      char *result = realpath(argv[argIter], resolved_path_buffer);

      puts(result);

      if (result != NULL) {
        free(result);
      }
    }
  }

  return 0;
}

Makefile:

#Makefile
OBJ = realpath.o

%.o: %.c
      $(CC) -c -o $@ $< $(CFLAGS)

realpath: $(OBJ)
      gcc -o $@ $^ $(CFLAGS)

Ardından aşağıdakilerle derleyin makeve yumuşak bir bağlantı kurun:
ln -s $(pwd)/realpath /usr/local/bin/realpath


sadece yapabilir gcc realpath.c -o /usr/local/bin/realpathmiyiz
Alexander Mills

1
@AlexanderMills Derleyiciyi root olarak çalıştırmamalısınız; ve sonra, yoksa, yazma ayrıcalıklarına sahip olmayacaksınız/usr/local/bin
2019

6

Bunu elde etmek için Python kullanın:

#!/usr/bin/env python
import os
import sys

print(os.path.realpath(sys.argv[1]))

2
abs_path () {    
   echo "$(cd $(dirname "$1");pwd)/$(basename "$1")"
}

dirname/path/to/fileyani dizin adını verir /path/to.

cd /path/to; pwd yolun mutlak olmasını sağlar.

basenamesadece içinde dosya adını verecektir /path/to/file, yani file.


Bu kod soruyu yanıtlayabilirken, bu kodun soruyu neden ve / veya nasıl yanıtladığına ilişkin ek bağlam sağlamak, uzun vadeli değerini artırır.
Igor F.

1

Yukarıda görebileceğiniz gibi, yaklaşık 6 ay önce buna bir atış yaptım. Kendimi tekrar benzer bir şeye muhtaç bulana kadar bunu tamamen unutmuşum. Ne kadar ilkel olduğunu görünce tamamen şok oldum ; Yaklaşık bir yıldır kendime oldukça yoğun bir şekilde kod yazmayı öğretiyorum, ancak çoğu zaman her şey en kötü haldeyken hiç bir şey öğrenmemiş olduğumu hissediyorum.

Yukarıdaki 'çözümü' kaldırırdım, ancak son birkaç ayda gerçekten ne kadar öğrendiğimin bir kaydı olmasını gerçekten çok seviyorum.

Ama ben daldım. Dün gece oturdum ve her şeyi çözdüm. Yorumlardaki açıklama yeterli olmalıdır. Çalışmaya devam ettiğim kopyayı takip etmek istiyorsanız, bu özü takip edebilirsiniz. Bu muhtemelen ihtiyacın olanı yapıyor.

#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.

## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
## dereference symbolic links (ala 'readlink') until the originating file
## is found. This is effectively the same function provided in stdlib.h as
## 'realpath' and on the command line in GNU 'readlink -f'.

## Neither of these tools, however, are particularly accessible on the many
## systems that do not have the GNU implementation of readlink, nor ship
## with a system compiler (not to mention the requisite knowledge of C).

## This script is written with portability and (to the extent possible, speed)
## in mind, hence the use of printf for echo and case statements where they
## can be substituded for test, though I've had to scale back a bit on that.

## It is (to the best of my knowledge) written in standard POSIX shell, and
## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
## issues with it, though I'm not sure why; so probably best to avoid for now.

## Particularly useful (in fact, the reason I wrote this) is the fact that
## it can be used within a shell script to find the path of the script itself.
## (I am sure the shell knows this already; but most likely for the sake of
## security it is not made readily available. The implementation of "$0"
## specificies that the $0 must be the location of **last** symbolic link in
## a chain, or wherever it resides in the path.) This can be used for some
## ...interesting things, like self-duplicating and self-modifiying scripts.

## Currently supported are three errors: whether the file specified exists
## (ala ENOENT), whether its target exists/is accessible; and the special
## case of when a sybolic link references itself "foo -> foo": a common error
## for beginners, since 'ln' does not produce an error if the order of link
## and target are reversed on the command line. (See POSIX signal ELOOP.)

## It would probably be rather simple to write to use this as a basis for
## a pure shell implementation of the 'symlinks' util included with Linux.

## As an aside, the amount of code below **completely** belies the amount
## effort it took to get this right -- but I guess that's coding for you.

##===-------------------------------------------------------------------===##

for argv; do :; done # Last parameter on command line, for options parsing.

## Error messages. Use functions so that we can sub in when the error occurs.

recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
errnoent(){ printf "No such file: "$@"\n" ;} # Borrow a horrible signal name.

# Probably best not to install as 'pathfull', if you can avoid it.

pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"

## 'test and 'ls' report different status for bad symlinks, so we use this.

 if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null;  then
    errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ];   then
    recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -z "$link" ]; then
    dangling 1>&2; exit 1; fi
 fi

## Not a link, but there might be one in the path, so 'cd' and 'pwd'.

 if [ -z "$link" ]; then if [ "$(dirname "$@" | cut -c1)" = '/' ]; then
   printf "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\n"; fi; exit 0
 fi

## Walk the symlinks back to the origin. Calls itself recursivly as needed.

 while [ "$link" ]; do
   cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
   case "$newlink" in
    "$link") dangling 1>&2 && exit 1                                       ;;
         '') printf "$(pwd)/$(basename "$link")\n"; exit 0                 ;;
          *) link="$newlink" && pathfull "$link"                           ;;
   esac
 done
 printf "$(pwd)/$(basename "$newlink")\n"
}

## Demo. Install somewhere deep in the filesystem, then symlink somewhere 
## else, symlink again (maybe with a different name) elsewhere, and link
## back into the directory you started in (or something.) The absolute path
## of the script will always be reported in the usage, along with "$0".

if [ -z "$argv" ]; then scriptname="$(pathfull "$0")"

# Yay ANSI l33t codes! Fancy.
 printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
 printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         \033[4m$scriptname\033[24m\n\n        "
 printf " From within an invocation of a script, locate the script's "
 printf "own file\n         (no matter where it has been linked or "
 printf "from where it is being called).\n\n"

else pathfull "$@"
fi

1
Temel bağlantı kopmuş görünüyor.
Alex

bu cevap işe yarar : stackoverflow.com/a/46772980/1223975 .... bunu kullanmalısın.
Alexander Mills

Uygulamanızın, ..üst referanslardan önce sembolik bağları çözmediğini unutmayın ; örneğin /foo/link_to_other_directory/.., /foosembolik bağın /foo/link_to_other_directoryişaret ettiği yolun ebeveyni yerine çözümlenir . readlink -fve kökten başlayıp önceden bekleyen bağlantı hedeflerini hala işlenmekte olan geri kalanla güncelleyen her bir yol bileşeninirealpath çözümleyin . Bu mantığı yeniden uygulayarak bu soruya bir yanıt ekledim.
Martijn Pieters

1

Mac OS X için realpath

realpath() {
    path=`eval echo "$1"`
    folder=$(dirname "$path")
    echo $(cd "$folder"; pwd)/$(basename "$path"); 
}

İlgili yol ile örnek:

realpath "../scripts/test.sh"

Ana klasör örneği

realpath "~/Test/../Test/scripts/test.sh"

1
güzel basit bir çözüm, sadece bir uyarı buldum, onunla birlikte çağırdığımda ..doğru cevabı vermiyor, bu yüzden verilen yolun bir dizin olup olmadığını kontrol ettim: if test -d $path ; then echo $(cd "$path"; pwd) ; else [...]
herbert

Oysa benim için iş değil mi "$(dirname $(dirname $(realpath $0)))"... Başka yüzden ihtiyaç şey çalışır
Alexander Mills

İşe yaramaz kullanımıecho da işlenen edilmemelidir.
üçlü

Bu aslında sembolik bağları gerçek yolun yapacağı şekilde çözmez. Sembolik bağları çözmeden önce , sonra değil, ..ana başvuruları çözer ; bunu homebrew kurulu olarak deneyin , bağlantı oluşturup çalıştırın ; bu baskılar . Ama sizin fonksiyonunuz onun yerine üretiyor , çünkü hala cd'den sonra gösteriliyor . coreutilsln -s /var/log /tmp/linkexamplerealpath /tmp/linkexample/..//private/var/tmp/linkexample/..pwd/tmp/linkexample
Martijn Pieters

1

MacOS'ta, sembolik bağlantıları güvenilir şekilde işleyen buna bulduğum tek çözüm kullanmaktır realpath. Bunu gerektirdiğinden brew install coreutils, bu adımı otomatikleştirdim. Uygulamam şuna benziyor:

#!/usr/bin/env bash

set -e

if ! which realpath >&/dev/null; then
  if ! which brew >&/dev/null; then
    msg="ERROR: This script requires brew. See https://brew.sh for installation instructions."
    echo "$(tput setaf 1)$msg$(tput sgr0)" >&2
    exit 1
  fi
  echo "Installing coreutils/realpath"
  brew install coreutils >&/dev/null
fi

thisDir=$( dirname "`realpath "$0"`" )
echo "This script is run from \"$thisDir\""


brewYüklenmemişlerse bu hatalar , ancak alternatif olarak bunu da yükleyebilirsiniz. Ağdan rastgele ruby ​​kodunu toplayan bir şeyi otomatikleştirmek konusunda kendimi rahat hissetmedim.

Bunun Oleg Mikheev'in cevabının otomatik bir varyasyonu olduğuna dikkat edin .


Önemli bir test

Bu çözümlerden herhangi birinin iyi bir testi şudur:

  1. kodu bir komut dosyasında bir yere koyun
  2. başka bir dizinde, ln -so dosyaya symlink ( )
  3. betiği bu sembolik bağdan çalıştır

Çözüm sembolik bağa başvuruyor ve size orijinal dizini veriyor mu? Eğer öyleyse, işe yarıyor.


0

Bu OSX için işe yarıyor gibi görünüyor, herhangi bir ikili dosya gerektirmiyor ve buradan çekildi

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

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

0

Bunu severim:

#!/usr/bin/env bash
function realpath() {
    local _X="$PWD"
    local _LNK=$1
    cd "$(dirname "$_LNK")"
    if [ -h "$_LNK" ]; then
        _LNK="$(readlink "$_LNK")"
        cd "$(dirname "$_LNK")"
    fi
    echo "$PWD/$(basename "$_LNK")"
    cd "$_X"
}

0

Ben gerekli realpathOS X üzerinde değiştirme, sembolik ve ebeveyn referansları olan yollarda doğru çalışır birini tıpkı readlink -fmisiniz? Öğesinin . Bu, üst referansları çözmeden önce yoldaki sembolik bağları çözmeyi içerir ; örneğin, homebrew coreutilsşişesini taktıysanız , ardından çalıştırın:

$ ln -s /var/log/cups /tmp/linkeddir  # symlink to another directory
$ greadlink -f /tmp/linkeddir/..      # canonical path of the link parent
/private/var/log

Not readlink -fçözmüştür /tmp/linkeddir önce çözülmesi ..ebeveyn dir referansı. Tabii ki, readlink -fMac'te de yok .

Yani bir bash uygulamasının parçası olarak Bash 3.2'de realpathbir GNUlib canonicalize_filename_mode(path, CAN_ALL_BUT_LAST)işlev çağrısının yaptığını yeniden uyguladım ; bu aynı zamanda GNU'nun readlink -fyaptığı işlev çağrısıdır :

# shellcheck shell=bash
set -euo pipefail

_contains() {
    # return true if first argument is present in the other arguments
    local elem value

    value="$1"
    shift

    for elem in "$@"; do 
        if [[ $elem == "$value" ]]; then
            return 0
        fi
    done
    return 1
}

_canonicalize_filename_mode() {
    # resolve any symlink targets, GNU readlink -f style
    # where every path component except the last should exist and is
    # resolved if it is a symlink. This is essentially a re-implementation
    # of canonicalize_filename_mode(path, CAN_ALL_BUT_LAST).
    # takes the path to canonicalize as first argument

    local path result component seen
    seen=()
    path="$1"
    result="/"
    if [[ $path != /* ]]; then  # add in current working dir if relative
        result="$PWD"
    fi
    while [[ -n $path ]]; do
        component="${path%%/*}"
        case "$component" in
            '') # empty because it started with /
                path="${path:1}" ;;
            .)  # ./ current directory, do nothing
                path="${path:1}" ;;
            ..) # ../ parent directory
                if [[ $result != "/" ]]; then  # not at the root?
                    result="${result%/*}"      # then remove one element from the path
                fi
                path="${path:2}" ;;
            *)
                # add this component to the result, remove from path
                if [[ $result != */ ]]; then
                    result="$result/"
                fi
                result="$result$component"
                path="${path:${#component}}"
                # element must exist, unless this is the final component
                if [[ $path =~ [^/] && ! -e $result ]]; then
                    echo "$1: No such file or directory" >&2
                    return 1
                fi
                # if the result is a link, prefix it to the path, to continue resolving
                if [[ -L $result ]]; then
                    if _contains "$result" "${seen[@]+"${seen[@]}"}"; then
                        # we've seen this link before, abort
                        echo "$1: Too many levels of symbolic links" >&2
                        return 1
                    fi
                    seen+=("$result")
                    path="$(readlink "$result")$path"
                    if [[ $path = /* ]]; then
                        # if the link is absolute, restart the result from /
                        result="/"
                    elif [[ $result != "/" ]]; then
                        # otherwise remove the basename of the link from the result
                        result="${result%/*}"
                    fi
                elif [[ $path =~ [^/] && ! -d $result ]]; then
                    # otherwise all but the last element must be a dir
                    echo "$1: Not a directory" >&2
                    return 1
                fi
                ;;
        esac
    done
    echo "$result"
}

Aynı (ara) yol iki kez görüldüğünde çıkarak dairesel sembolik bağlantı algılamasını içerir.

Tek ihtiyacınız olan readlink -fşeyse, yukarıdakileri şu şekilde kullanabilirsiniz:

readlink() {
    if [[ $1 != -f ]]; then  # poor-man's option parsing
        # delegate to the standard readlink command
        command readlink "$@"
        return
    fi

    local path result seenerr
    shift
    seenerr=
    for path in "$@"; do
        # by default readlink suppresses error messages
        if ! result=$(_canonicalize_filename_mode "$path" 2>/dev/null); then
            seenerr=1
            continue
        fi
        echo "$result"
    done
    if [[ $seenerr ]]; then
        return 1;
    fi
}

İçin realpath, ben de gerekli --relative-tove --relative-basestandart hale sonra size göreli yolları vermek olan desteği:

_realpath() {
    # GNU realpath replacement for bash 3.2 (OS X)
    # accepts --relative-to= and --relative-base options
    # and produces canonical (relative or absolute) paths for each
    # argument on stdout, errors on stderr, and returns 0 on success
    # and 1 if at least 1 path triggered an error.

    local relative_to relative_base seenerr path

    relative_to=
    relative_base=
    seenerr=

    while [[ $# -gt 0 ]]; do
        case $1 in
            "--relative-to="*)
                relative_to=$(_canonicalize_filename_mode "${1#*=}")
                shift 1;;
            "--relative-base="*)
                relative_base=$(_canonicalize_filename_mode "${1#*=}")
                shift 1;;
            *)
                break;;
        esac
    done

    if [[
        -n $relative_to
        && -n $relative_base
        && ${relative_to#${relative_base}/} == "$relative_to"
    ]]; then
        # relative_to is not a subdir of relative_base -> ignore both
        relative_to=
        relative_base=
    elif [[ -z $relative_to && -n $relative_base ]]; then
        # if relative_to has not been set but relative_base has, then
        # set relative_to from relative_base, simplifies logic later on
        relative_to="$relative_base"
    fi

    for path in "$@"; do
        if ! real=$(_canonicalize_filename_mode "$path"); then
            seenerr=1
            continue
        fi

        # make path relative if so required
        if [[
            -n $relative_to
            && ( # path must not be outside relative_base to be made relative
                -z $relative_base || ${real#${relative_base}/} != "$real"
            )
        ]]; then
            local common_part parentrefs

            common_part="$relative_to"
            parentrefs=
            while [[ ${real#${common_part}/} == "$real" ]]; do
                common_part="$(dirname "$common_part")"
                parentrefs="..${parentrefs:+/$parentrefs}"
            done

            if [[ $common_part != "/" ]]; then
                real="${parentrefs:+${parentrefs}/}${real#${common_part}/}"
            fi
        fi

        echo "$real"
    done
    if [[ $seenerr ]]; then
        return 1
    fi
}

if ! command -v realpath > /dev/null 2>&1; then
    # realpath is not available on OSX unless you install the `coreutils` brew
    realpath() { _realpath "$@"; }
fi

Bu kod için Kod İnceleme isteğime birim testleri ekledim .


-2

Yorumcu ile olan iletişime dayanarak, bunun çok zor olduğunu ve bir realpath uygulamasının üç değerlik bir yolu olmadığını, Ubuntu ile tamamen aynı şekilde davrandığını kabul ettim.

Ancak aşağıdaki sürüm, macbook'taki günlük ihtiyaçlarımı karşılayamayan ve en iyi cevapları karşılayamayan köşe vakalarını ele alabilir. Bu kodu ~ / .bashrc dosyanıza koyun ve şunu unutmayın:

  • arg yalnızca 1 dosya veya dizin olabilir, joker karakter kullanılamaz
  • dizinde veya dosya adında boşluk yok
  • en azından dosya veya dizinin üst dizini var
  • kullanmaktan çekinmeyin. .. / şey, bunlar güvenli

    # 1. if is a dir, try cd and pwd
    # 2. if is a file, try cd its parent and concat dir+file
    realpath() {
     [ "$1" = "" ] && return 1

     dir=`dirname "$1"`
     file=`basename "$1"`

     last=`pwd`

     [ -d "$dir" ] && cd $dir || return 1
     if [ -d "$file" ];
     then
       # case 1
       cd $file && pwd || return 1
     else
       # case 2
       echo `pwd`/$file | sed 's/\/\//\//g'
     fi

     cd $last
    }

Gereksiz kullanımdanecho kaçınmak istiyorsunuz . Sadece pwdaynı yapar echo $(pwd)karkasın ikinci kopyasını yumurtlamaya gerek kalmadan. Ayrıca, argümanından alıntı yapmamak echobir hatadır (baştaki veya sondaki beyaz boşlukları, bitişik iç boşluk karakterlerini ve genişletilmiş joker karakterleri vb. Kaybedersiniz). Daha fazla stackoverflow.com/questions/10067266/…
tripleee

Ayrıca, var olmayan yollar için davranış hatalı; ama sanırım "ama hatırla" cümlesinin söylemeye çalıştığı şey bu. Ubuntu'daki davranış, realpathvar olmayan bir dizini talep ettiğinizde kesinlikle geçerli dizini yazdırmamaktır .
üçlü

Tutarlılık için, muhtemelen dir=$(dirname "$1"); file=$(basename "$1")uzun süredir eskimiş olan backtick sözdizimi yerine tercih edin. Ayrıca argümanların doğru alıntılanmasına yine dikkat edin.
üçlü

Güncellenmiş cevabınız, hataların çoğunu düzeltmekte başarısız görünüyor ve yenilerini ekliyor.
üçlü

lütfen bana bazı başarısız durumlar verin, çünkü ubuntu 18.04 masaüstünde yaptığım tüm testler tamam, teşekkürler.
Occia
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.