Jak rozwiązywać dowiązania symboliczne w skrypcie powłoki

Biorąc pod uwagę ścieżkę bezwzględną lub względną (w systemie Uniksopodobnym), chciałbym określić pełną ścieżkę docelową po rozwiązaniu wszelkich pośrednich dowiązań symbolicznych. Punkty bonusowe za jednocześnie rozwiązywanie zapisów ~username.

Jeśli celem jest katalog, może być możliwe, aby chdir() do katalogu, a następnie wywołać getcwd (), ale naprawdę chcę to zrobić ze skryptu powłoki, a nie pisania helpera C. Niestety, muszle mają tendencję do próbowania Ukryj istnienie dowiązań symbolicznych przed użytkownikiem (jest to bash na OS X):

$ ls -ld foo bar
drwxr-xr-x   2 greg  greg  68 Aug 11 22:36 bar
lrwxr-xr-x   1 greg  greg   3 Aug 11 22:36 foo -> bar
$ cd foo
$ pwd
/Users/greg/tmp/foo
$

Chcę funkcji resolve() takiej, że gdy zostanie wykonana z katalogu tmp w powyższym przykładzie, resolve("foo") == "/Users/greg/tmp/bar".

Author: Greg Hewgill, 2008-08-11

19 answers

Zgodnie ze standardami, {[0] } powinna zwracać ścieżkę z rozwiązanymi dowiązaniami symbolicznymi.

C Funkcja char *getcwd(char *buf, size_t size) z unistd.h powinna mieć takie samo zachowanie.

Getcwd pwd

 94
Author: kauppi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-21 18:41:11
readlink -f "$path"

Uwaga wydawcy: powyższe działa z GNU readlink i FreeBSD / PC-BSD / OpenBSD readlink, ale nie {[19] } na OS X od 10.11.
GNU readlink oferuje dodatkowe, powiązane opcje, takie jak -m do rozwiązywania dowiązania symbolicznego, niezależnie od tego, czy istnieje ostateczny cel.

Uwaga od wersji GNU coreutils 8.15 (2012-01-06) dostępny jest program realpath, który jest mniej rozbudowany i bardziej elastyczny niż powyższy. Jest również kompatybilny z Utilem FreeBSD o tej samej nazwie. Zawiera również funkcję generowania względnej ścieżki między dwoma plikami.

realpath $path

[admin Dodany poniżej z komentarza przez halloleo -danorton]

Dla Mac OS X (przez co najmniej 10.11.x), Użyj readlink bez opcji -f:

readlink $path

Uwaga redaktora: to nie rozwiąże dowiązań symbolicznych rekurencyjnie, a tym samym nie zgłosi docelowego ultimate ; np. given dowiązania symbolicznego a to wskazuje na b, co z kolei wskazuje na c, to tylko raportuje b (i nie zapewnia, że jest to wyjście jako ścieżka absolutna).
Użyj następującego polecenia perl w systemie OS X, aby wypełnić lukę brakującej funkcji readlink -f:
perl -MCwd -le 'print Cwd::abs_path(shift)' "$path"

 415
Author: pixelbeat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-22 22:46:51

"pwd-P" wydaje się działać, jeśli chcesz tylko katalog, ale jeśli z jakiegoś powodu chcesz nazwę rzeczywistego pliku wykonywalnego, nie sądzę, że to pomaga. Oto moje rozwiązanie:

#!/bin/bash

# get the absolute path of the executable
SELF_PATH=$(cd -P -- "$(dirname -- "$0")" && pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "$0")

# resolve symlinks
while [[ -h $SELF_PATH ]]; do
    # 1) cd to directory of the symlink
    # 2) cd to the directory of where the symlink points
    # 3) get the pwd
    # 4) append the basename
    DIR=$(dirname -- "$SELF_PATH")
    SYM=$(readlink "$SELF_PATH")
    SELF_PATH=$(cd "$DIR" && cd "$(dirname -- "$SYM")" && pwd)/$(basename -- "$SYM")
done
 26
Author: tlrobinson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-18 13:24:01

Jednym z moich ulubionych jest realpath foo

realpath - return the canonicalized absolute pathname

realpath  expands  all  symbolic  links  and resolves references to '/./', '/../' and extra '/' characters in the null terminated string named by path and
       stores the canonicalized absolute pathname in the buffer of size PATH_MAX named by resolved_path.  The resulting path will have no symbolic link, '/./' or
       '/../' components.
 17
Author: Gregory,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-04 23:39:05
readlink -e [filepath]

Wydaje się być dokładnie tym, o co prosisz - przyjmuje ścieżkę arbir, rozwiązuje wszystkie dowiązania symboliczne i zwraca ścieżkę "rzeczywistą" - i to jest "standard * nix", że prawdopodobnie wszystkie systemy mają już

 10
Author: Chuck Kollars,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-09 14:34:38

Inny sposób:

# Gets the real path of a link, following all links
myreadlink() { [ ! -h "$1" ] && echo "$1" || (local link="$(expr "$(command ls -ld -- "$1")" : '.*-> \(.*\)$')"; cd $(dirname $1); myreadlink "$link" | sed "s|^\([^/].*\)\$|$(dirname $1)/\1|"); }

# Returns the absolute path to a command, maybe in $PATH (which) or not. If not found, returns the same
whereis() { echo $1 | sed "s|^\([^/].*/.*\)|$(pwd)/\1|;s|^\([^/]*\)$|$(which -- $1)|;s|^$|$1|"; } 

# Returns the realpath of a called command.
whereis_realpath() { local SCRIPT_PATH=$(whereis $1); myreadlink ${SCRIPT_PATH} | sed "s|^\([^/].*\)\$|$(dirname ${SCRIPT_PATH})/\1|"; } 
 5
Author: Keymon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-13 10:52:44

Złożenie niektórych z podanych rozwiązań razem, wiedząc, że readlink jest dostępny na większości systemów, ale wymaga innych argumentów, działa to dobrze dla mnie na OSX i Debianie. Nie jestem pewien co do systemów BSD. Może warunek musi być [[ $OSTYPE != darwin* ]], aby wykluczyć -f tylko z OSX.

#!/bin/bash
MY_DIR=$( cd $(dirname $(readlink `[[ $OSTYPE == linux* ]] && echo "-f"` $0)) ; pwd -P)
echo "$MY_DIR"
 5
Author: hpvw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-14 07:47:17

Oto jak można uzyskać rzeczywistą ścieżkę do pliku w systemie MacOS / Unix za pomocą wbudowanego skryptu Perla:

FILE=$(perl -e "use Cwd qw(abs_path); print abs_path('$0')")

Podobnie, aby uzyskać katalog symbolicznego pliku:

DIR=$(perl -e "use Cwd qw(abs_path); use File::Basename; print dirname(abs_path('$0'))")
 4
Author: Igor Afanasyev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-30 03:57:48

Czy Twoja ścieżka jest katalogiem, czy może plikiem? Jeśli jest to katalog, to jest to proste:

(cd "$DIR"; pwd -P)

Jednak, jeśli to może być plik, to nie zadziała:

DIR=$(cd $(dirname "$FILE"); pwd -P); echo "${DIR}/$(readlink "$FILE")"

Ponieważ dowiązanie symboliczne może zostać rozdzielone na ścieżkę względną lub pełną.

Na skryptach muszę znaleźć prawdziwą ścieżkę, aby móc odwołać się do konfiguracji lub innych skryptów zainstalowanych razem z nim, używam tego:

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done

Można ustawić SOURCE na dowolną ścieżkę do pliku. Zasadniczo, tak długo, jak ścieżka jest dowiązaniem symbolicznym, rozwiązuje ten dowiązanie symboliczne. Sztuczka jest w ostatniej linii pętli. Jeśli rozwiązane dowiązanie symboliczne jest bezwzględne, użyje go jako SOURCE. Jednak, jeśli jest względna, będzie poprzedzać DIR dla niego, który został rozwiązany w rzeczywistym miejscu przez prosty trick, który pierwszy opisał.

 3
Author: Daniel C. Sobral,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-28 18:00:47

Typowe skrypty powłoki często muszą znaleźć swój katalog "home", nawet jeśli są wywoływane jako dowiązanie symboliczne. Skrypt musi więc znaleźć swoją "prawdziwą" pozycję już od $0.

cat `mvn`

W moim systemie drukuje skrypt zawierający następujące informacje, które powinny być dobrą wskazówką na to, czego potrzebujesz.

if [ -z "$M2_HOME" ] ; then
  ## resolve links - $0 may be a link to maven's home
  PRG="$0"

  # need this for relative symlinks
  while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
      PRG="$link"
    else
      PRG="`dirname "$PRG"`/$link"
    fi
  done

  saveddir=`pwd`

  M2_HOME=`dirname "$PRG"`/..

  # make it fully qualified
  M2_HOME=`cd "$M2_HOME" && pwd`
 2
Author: Hugo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-11 11:17:23
function realpath {
    local r=$1; local t=$(readlink $r)
    while [ $t ]; do
        r=$(cd $(dirname $r) && cd $(dirname $t) && pwd -P)/$(basename $t)
        t=$(readlink $r)
    done
    echo $r
}

#example usage
SCRIPT_PARENT_DIR=$(dirname $(realpath "$0"))/..
 2
Author: Dave,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-10 14:52:57

Uwaga: uważam, że jest to solidne, przenośne, gotowe rozwiązanie, które jest niezmiennie długie właśnie z tego powodu.

Poniżej znajduje się W Pełni zgodny z POSIX skrypt / funkcja , czyli wieloplatformowy (działa również na macOS, którego readlink nadal nie obsługuje -fod wersji 10.12 (Sierra))-używa tylko funkcji języka powłoki POSIX i tylko wywołań narzędzi zgodnych z POSIX.

Jest to przenośny implementacja GNU readlink -e (bardziej rygorystyczna wersja readlink -f).

Możesz uruchomić skrypt Z sh lub źródło funkcja W bash, ksh, oraz zsh:

Na przykład wewnątrz skryptu można użyć go w następujący sposób, aby uzyskać prawdziwy katalog źródłowy uruchomionego skryptu z rozwiązanymi dowiązaniami symbolicznymi:

trueScriptDir=$(dirname -- "$(rreadlink "$0")")

rreadlink definicja skryptu / funkcji:

kod został zaadaptowany z wdzięcznością z ta odpowiedź .
Stworzyłem również autonomiczną wersję narzędzia bash tutaj , którą można zainstalować za pomocą
npm install rreadlink -g, jeśli masz węzeł.js zainstalowany.

#!/bin/sh

# SYNOPSIS
#   rreadlink <fileOrDirPath>
# DESCRIPTION
#   Resolves <fileOrDirPath> to its ultimate target, if it is a symlink, and
#   prints its canonical path. If it is not a symlink, its own canonical path
#   is printed.
#   A broken symlink causes an error that reports the non-existent target.
# LIMITATIONS
#   - Won't work with filenames with embedded newlines or filenames containing 
#     the string ' -> '.
# COMPATIBILITY
#   This is a fully POSIX-compliant implementation of what GNU readlink's
#    -e option does.
# EXAMPLE
#   In a shell script, use the following to get that script's true directory of origin:
#     trueScriptDir=$(dirname -- "$(rreadlink "$0")")
rreadlink() ( # Execute the function in a *subshell* to localize variables and the effect of `cd`.

  target=$1 fname= targetDir= CDPATH=

  # Try to make the execution environment as predictable as possible:
  # All commands below are invoked via `command`, so we must make sure that
  # `command` itself is not redefined as an alias or shell function.
  # (Note that command is too inconsistent across shells, so we don't use it.)
  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not 
  # even have an external utility version of it (e.g, Ubuntu).
  # `command` bypasses aliases and shell functions and also finds builtins 
  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for
  # that to happen.
  { \unalias command; \unset -f command; } >/dev/null 2>&1
  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.

  while :; do # Resolve potential symlinks until the ultimate target is found.
      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
      fname=$(command basename -- "$target") # Extract filename.
      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
      if [ -L "$fname" ]; then
        # Extract [next] target path, which may be defined
        # *relative* to the symlink's own directory.
        # Note: We parse `ls -l` output to find the symlink target
        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
        target=$(command ls -l "$fname")
        target=${target#* -> }
        continue # Resolve [next] symlink target.
      fi
      break # Ultimate target reached.
  done
  targetDir=$(command pwd -P) # Get canonical dir. path
  # Output the ultimate target's canonical path.
  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
  if [ "$fname" = '.' ]; then
    command printf '%s\n' "${targetDir%/}"
  elif  [ "$fname" = '..' ]; then
    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
    # AFTER canonicalization.
    command printf '%s\n' "$(command dirname -- "${targetDir}")"
  else
    command printf '%s\n' "${targetDir%/}/$fname"
  fi
)

rreadlink "$@"

Styczna na bezpieczeństwo:

Jarno , w odniesieniu do funkcji zapewniającej, że builtin command nie jest zasłonięty przez alias lub funkcję powłoki o tej samej nazwie, pyta w komentarzu:

Co jeśli unalias lub unset i [ są ustawione jako aliasy czy funkcje powłoki?

Motywacją stojącą za rreadlink zapewnianiem, że commandma swoje pierwotne znaczenie jest użycie go do ominięcia (łagodnego) wygody aliasów i funkcji często używanych do cieniowania standardowych poleceń w interaktywnych powłokach, takich jak przedefiniowanie ls w celu włączenia ulubionych opcji.

Chyba, że masz do czynienia z niezaufanym, złośliwym środowiskiem, martwiąc się o unaliaslub unset - lub, while, do, ... - bycie przedefiniowanym nie jest problemem.

Istniejecoś na czym funkcja musi polegać, aby mieć swoje pierwotne znaczenie i zachowanie - nie ma na to sposobu.
To, że powłoki podobne do POSIX pozwalają na redefinicję wbudowanych, a nawet językowych słów kluczowych, jest z natury zagrożeniem bezpieczeństwa (a pisanie paranoidalnego kodu jest trudne w ogóle).

Aby konkretnie odpowiedzieć na twoje obawy:

Funkcja polega na tym, że unalias i unset mają swoje pierwotne znaczenie. Ponowne zdefiniowanie ich jako funkcji powłoki w sposób, który zmienia ich zachowanie, byłoby problemem; ponowne zdefiniowanie jako alias jest nie koniecznie jest to problemem, ponieważ cytowanie (części) nazwy polecenia (np. \unalias) omija aliasy.

Jednak cytowanie jest Nie opcją dla słów kluczowych shell (while, for, if, do, ...) i podczas gdy słowa kluczowe powłoki mają pierwszeństwo przed funkcjami powłoki , W bash i zsh aliasy mają najwyższy priorytet, więc aby chronić przed redefinicjami słów kluczowych powłoki, musisz uruchomić unalias z ich nazwami (chociaż w Nie-interaktywne bash aliasy powłok (takich jak skrypty) są Nie rozwijane domyślnie - tylko jeśli {[35] } jest jawnie wywołane jako pierwsze).

Aby upewnić się, że unalias - jako wbudowany - ma swoje pierwotne znaczenie, musisz najpierw użyć \unset na nim, co wymaga, abyunset miało swoje pierwotne znaczenie: {46]}

unset jest skorupą wbudowany , więc aby mieć pewność, że jest wywoływana jako taka, musisz upewnić się, że sama nie jest przedefiniowana jako funkcja . Podczas gdy można ominąć formularz aliasu za pomocą cytowania, nie można ominąć formularza funkcji powłoki-catch 22.

Tak więc, jeśli nie możesz polegać na unset, aby mieć jego pierwotne znaczenie, z tego, co mogę powiedzieć, nie ma gwarantowanego sposobu obrony przed wszelkimi złośliwymi redefinicjami.

 2
Author: mklement0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:03:05

Spróbuj tego:

cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))
 1
Author: diyism,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-29 03:05:19

Ponieważ spotykałem się z tym wiele razy na przestrzeni lat i tym razem potrzebowałem czystej wersji Bash portable, której mógłbym użyć na OSX i Linuksie, poszedłem dalej i napisałem jedną: {]}

Mieszka tutaj Wersja żywa:

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

Ale ze względu na tak, oto obecna wersja (uważam, że jest dobrze przetestowana..ale jestem otwarty na opinie!)

Może nie być trudne, aby to działało dla zwykłej powłoki bourne ' a (sh), ale nie próbowałem...Za bardzo lubię $FUNCNAME. :)

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

Oto klasyczny przykład, dzięki brew:

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

Użyj tej funkcji, a zwróci-real-path:

%% 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 
 1
Author: keen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-24 21:06:54

Aby obejść niekompatybilność Maca, wymyśliłem

echo `php -r "echo realpath('foo');"`

Nie wielki ale cross OS

 1
Author: Clemens Tolboom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-21 18:43:19

W przypadku, gdy nie można użyć pwd (np. wywołanie skryptu z innej lokalizacji), użyj realpath (z lub bez dirname):

$(dirname $(realpath $PATH_TO_BE_RESOLVED))

Działa zarówno podczas wywołania przez (wiele) dowiązań symbolicznych, jak i podczas bezpośredniego wywołania skryptu-z dowolnego miejsca.

 1
Author: nakwa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-25 13:20:39

Jest to rozwiązywanie dowiązań symbolicznych w Bash, które działa niezależnie od tego, czy dowiązanie jest katalogiem, czy nie:

function readlinks {(
  set -o errexit -o nounset
  declare n=0 limit=1024 link="$1"

  # If it's a directory, just skip all this.
  if cd "$link" 2>/dev/null
  then
    pwd -P
    return 0
  fi

  # Resolve until we are out of links (or recurse too deep).
  while [[ -L $link ]] && [[ $n -lt $limit ]]
  do
    cd "$(dirname -- "$link")"
    n=$((n + 1))
    link="$(readlink -- "${link##*/}")"
  done
  cd "$(dirname -- "$link")"

  if [[ $n -ge $limit ]]
  then
    echo "Recursion limit ($limit) exceeded." >&2
    return 2
  fi

  printf '%s/%s\n' "$(pwd -P)" "${link##*/}"
)}

Zauważ, że wszystkie cd i set rzeczy odbywają się w subshell.

 1
Author: solidsnack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-24 04:22:01

Tutaj przedstawiam to, co uważam za wieloplatformowe (przynajmniej Linux i macOS) rozwiązanie odpowiedzi, która działa dobrze dla mnie obecnie.

crosspath()
{
    local ref="$1"
    if [ -x "$(which realpath)" ]; then
        path="$(realpath "$ref")"
    else
        path="$(readlink -f "$ref" 2> /dev/null)"
        if [ $? -gt 0 ]; then
            if [ -x "$(which readlink)" ]; then
                if [ ! -z "$(readlink "$ref")" ]; then
                    ref="$(readlink "$ref")"
                fi
            else
                echo "realpath and readlink not available. The following may not be the final path." 1>&2
            fi
            if [ -d "$ref" ]; then
                path="$(cd "$ref"; pwd -P)"
            else
                path="$(cd $(dirname "$ref"); pwd -P)/$(basename "$ref")"
            fi
        fi
    fi
    echo "$path"
}

Oto macOS (tylko?) rozwiązanie. Prawdopodobnie lepiej pasuje do pierwotnego pytania.

mac_realpath()
{
    local ref="$1"
    if [[ ! -z "$(readlink "$ref")" ]]; then
        ref="$(readlink "$1")"
    fi
    if [[ -d "$ref" ]]; then
        echo "$(cd "$ref"; pwd -P)"
    else
        echo "$(cd $(dirname "$ref"); pwd -P)/$(basename "$ref")"
    fi
}
 0
Author: RuneImp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-01-14 10:32:08

Moja odpowiedź tutaj Bash: jak uzyskać prawdziwą ścieżkę dowiązania symbolicznego?

Ale w skrócie bardzo przydatne w skryptach:

script_home=$( dirname $(realpath "$0") )
echo Original script home: $script_home

Są one częścią GNU coreutils, nadającą się do użytku w systemach Linux.

Aby wszystko przetestować, dodajemy dowiązanie symboliczne do /home / test2/, poprawiamy kilka dodatkowych rzeczy i uruchamiamy / wywołujemy z katalogu głównego:

/$ /home/test2/symlink
/home/test
Original script home: /home/test

Gdzie

Original script is: /home/test/realscript.sh
Called script is: /home/test2/symlink
 0
Author: Arunas Bartisius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-03 14:01:43