Come posso ottenere il comportamento del readlink di GNU -f su un Mac?


377

Su Linux, l' readlinkutilità accetta un'opzione -fche segue collegamenti aggiuntivi. Questo non sembra funzionare su Mac e probabilmente su sistemi basati su BSD. Quale sarebbe l'equivalente?

Ecco alcune informazioni di debug:

$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]

1
Un po 'in ritardo, ma alla tua domanda manca qualsiasi menzione della shell che usi. Questo è rilevante, perché readlinkpuò essere un comando incorporato o esterno.
0xC0000022L

1
Ciò spiegherebbe forse la differenza. Sono abbastanza sicuro di aver usato bash in entrambe le occasioni.
Troelskn,

1
Perché questa opzione è illegale sui Mac?
CommaToast,

3
Vorrei davvero che Apple facesse in modo che OS X supportasse più percorsi linux predefiniti e affrontasse cose come questa. Potrebbero farlo senza rompere nulla, no?
CommaToast,

1
@CommaToast Bene, spediscono con Perl così ++ :-) ... apri Terminal.app e digita: touch myfile ; ln -s myfile otherfile ; perl -MCwd=abs_path -le 'print abs_path readlink(shift);' otherfile... nel mio caso vedo: / Users / cito / myfile`. Aggiunto alla mia risposta di seguito. Saluti.
G. Cito,

Risposte:


181

readlink -f fa due cose:

  1. Esegue una sequenza di collegamenti simbolici fino a quando non trova un file effettivo.
  2. Restituisce il nome canonico di quel file, ovvero il suo nome di percorso assoluto.

Se vuoi, puoi semplicemente creare uno script di shell che usi il comportamento vanlink readlink per ottenere la stessa cosa. Ecco un esempio Ovviamente potresti inserirlo nel tuo script dove desideri chiamarereadlink -f

#!/bin/sh

TARGET_FILE=$1

cd `dirname $TARGET_FILE`
TARGET_FILE=`basename $TARGET_FILE`

# Iterate down a (possible) chain of symlinks
while [ -L "$TARGET_FILE" ]
do
    TARGET_FILE=`readlink $TARGET_FILE`
    cd `dirname $TARGET_FILE`
    TARGET_FILE=`basename $TARGET_FILE`
done

# Compute the canonicalized name by finding the physical path 
# for the directory we're in and appending the target file.
PHYS_DIR=`pwd -P`
RESULT=$PHYS_DIR/$TARGET_FILE
echo $RESULT

Si noti che ciò non include alcuna gestione degli errori. Di particolare importanza, non rileva i cicli di collegamento simbolico. Un modo semplice per farlo sarebbe quello di contare il numero di volte che fai il giro del ciclo e fallisci se colpisci un numero improbabilmente grande, come 1.000.

MODIFICATO da utilizzare pwd -Panziché $PWD.

Si noti che questo si aspetta script per essere richiamato come ./script_name filename, no -f, il cambiamento $1per $2se si vuole essere in grado di utilizzare con -f filenamecome GNU readlink.


1
Per quanto ne so, non funzionerà se una directory principale del percorso è un collegamento simbolico. Per esempio. se foo -> /var/cux, quindi foo/barnon verrà risolto, perché barnon è un collegamento, sebbene lo foosia.
troelskn,

1
Ah. Sì. Non è così semplice ma è possibile aggiornare lo script sopra per gestirlo. Modificherò (riscrivo, davvero) la risposta di conseguenza.
Keith Smith,

Bene, un collegamento potrebbe essere ovunque nel percorso. Immagino che la sceneggiatura possa iterare su ogni parte del percorso, ma allora diventa un po 'complicata.
troelskn,

1
Grazie. Il problema è che $ PWD ci sta fornendo la directory di lavoro logica, basata sui valori nei link simbolici che abbiamo seguito. Possiamo ottenere la vera directory fisica con 'pwd -P'. Dovrebbe calcolarla inseguendo ".." fino alla radice del file system. Aggiornerò di conseguenza lo script nella mia risposta.
Keith Smith,

12
Ottima soluzione, ma non gestisce correttamente i percorsi che devono essere sottoposti a escape , come i nomi di file o directory con spazi incorporati . Per ovviare a questo, l'uso cd "$(dirname "$TARGET_FILE")" e la TARGET_FILE=$(readlink "$TARGET_FILE")e TARGET_FILE=$(basename "$TARGET_FILE")nei luoghi appropriati nel codice sopra.
mklement0

439

MacPorts e Homebrew forniscono un pacchetto coreutils contenente greadlink(GNU readlink). Ringraziamo il post di Michael Kallweitt su mackb.com.

brew install coreutils

greadlink -f file.txt

Se stai chiamando readlink da luoghi diversi da bash, un'altra soluzione sarebbe rimuovere / usr / bin / readlink, quindi creare un collegamento a / usr / local / bin / greadlink.
chinglun,

8
Per favore non, a meno che tu non a) scrivi un software per te b) vuoi fare casino con gli altri. Scrivilo in modo tale che "funzioni" per chiunque.
kgadek,

14
@ching Fallo invece nel tuo .bashrc:export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
bfontaine

1
La domanda richiede l'equivalente su un sistema OS X o BSD, questo non risponde. Dovrai anche installare Homebrew. Installerai anche molte cose oltre a GNU Readlink. Se stai scrivendo uno script per installazioni OS X standard, questa risposta non sarà di aiuto. Puoi usare una combinazione di pwd -Pe readlinksu OS X, senza installare nulla
Jason S

4
Volevo GNU e altri programmi di utilità sul mio Mac che credo facciano così bene, senza il prefisso, e facilmente accessibili dal mio utente PATH. Ho fatto quanto sopra a partire da brew install <package> --default-names. Molte volte a una domanda su questo sito è stata data una risposta leggermente, o più, al di fuori dei requisiti tecnici del richiedente, ma in una situazione simile, ed è stata ancora molto utile. topbug.net/blog/2013/04/14/…
Pysis

43

Potresti essere interessato realpath(3)o di Python os.path.realpath. I due non sono esattamente gli stessi; la chiamata alla libreria C richiede l'esistenza di componenti del percorso intermedio, mentre la versione di Python no.

$ pwd
/tmp/foo
$ ls -l
total 16
-rw-r--r--  1 miles    wheel  0 Jul 11 21:08 a
lrwxr-xr-x  1 miles    wheel  1 Jul 11 20:49 b -> a
lrwxr-xr-x  1 miles    wheel  1 Jul 11 20:49 c -> b
$ python -c 'import os,sys;print(os.path.realpath(sys.argv[1]))' c
/private/tmp/foo/a

So che hai detto che preferiresti qualcosa di più leggero di un altro linguaggio di scripting, ma nel caso in cui compilare un binario sia insopportabile, puoi usare Python e ctypes (disponibili su Mac OS X 10.5) per concludere la chiamata in libreria:

#!/usr/bin/python

import ctypes, sys

libc = ctypes.CDLL('libc.dylib')
libc.realpath.restype = ctypes.c_char_p
libc.__error.restype = ctypes.POINTER(ctypes.c_int)
libc.strerror.restype = ctypes.c_char_p

def realpath(path):
    buffer = ctypes.create_string_buffer(1024) # PATH_MAX
    if libc.realpath(path, buffer):
        return buffer.value
    else:
        errno = libc.__error().contents.value
        raise OSError(errno, "%s: %s" % (libc.strerror(errno), buffer.value))

if __name__ == '__main__':
    print realpath(sys.argv[1])

Ironia della sorte, la versione C di questo script dovrebbe essere più breve. :)


Sì, realpathè davvero quello che voglio. Ma sembra piuttosto imbarazzante che devo compilare un binario per ottenere questa funzione da uno script di shell.
troelskn,

4
Perché allora non usare il one-liner Python nello script della shell? (Non è così diverso da una chiamata su una linea a readlinkse stesso, vero?)
Telemaco,

3
python -c "import os,sys; print(os.path.realpath(os.path.expanduser(sys.argv[1])))" "${1}"funziona con percorsi come'~/.symlink'
Wes Turner,

@troelskin Non mi ero reso conto che Perl, Python, ecc. fossero "ammessi" !! ... in tal caso aggiungerò perl -MCwd=abs_path -le 'print abs_path readlink(shift);'alla mia risposta :-)
G. Cito,

27

Odio continuare con un'altra implementazione, ma avevo bisogno di a) un'implementazione portatile, pure shell , e b) copertura unit-test , in quanto il numero di casi limite per qualcosa di simile non è banale .

Vedi il mio progetto su Github per test e codice completo. Quella che segue è una sinossi dell'implementazione:

Come Keith Smith sottolinea astutamente, readlink -ffa due cose: 1) risolve ricorsivamente i link simbolici e 2) canonicalizza il risultato, quindi:

realpath() {
    canonicalize_path "$(resolve_symlinks "$1")"
}

Innanzitutto, l'implementazione del resolver symlink:

resolve_symlinks() {
    local dir_context path
    path=$(readlink -- "$1")
    if [ $? -eq 0 ]; then
        dir_context=$(dirname -- "$1")
        resolve_symlinks "$(_prepend_path_if_relative "$dir_context" "$path")"
    else
        printf '%s\n' "$1"
    fi
}

_prepend_path_if_relative() {
    case "$2" in
        /* ) printf '%s\n' "$2" ;;
         * ) printf '%s\n' "$1/$2" ;;
    esac 
}

Si noti che questa è una versione leggermente semplificata dell'implementazione completa . L'implementazione completa aggiunge un piccolo controllo per i cicli di collegamento simbolico e massaggia un po 'l'output.

Infine, la funzione per canonicalizzare un percorso:

canonicalize_path() {
    if [ -d "$1" ]; then
        _canonicalize_dir_path "$1"
    else
        _canonicalize_file_path "$1"
    fi
}   

_canonicalize_dir_path() {
    (cd "$1" 2>/dev/null && pwd -P) 
}           

_canonicalize_file_path() {
    local dir file
    dir=$(dirname -- "$1")
    file=$(basename -- "$1")
    (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
}

Questo è tutto, più o meno. Abbastanza semplice da incollare nel tuo script, ma abbastanza complicato da farti impazzire per fare affidamento su qualsiasi codice che non ha unit test per i tuoi casi d'uso.


3
Questo non è POSIX - utilizza la localparola chiave non POSIX
go2null

Questo non equivale a readlink -f, né a realpath. Supponendo che un Mac con la formula coreutils installata (quindi con greadlinkdisponibile), prova con ln -s /tmp/linkeddir ~/Documents; greadlink -f /tmp/linkeddir/..stampa la tua home directory (ha risolto il /tmp/linkeddircollegamento prima di applicare il riferimento alla directory ..principale), ma il tuo codice produce /private/tmp/come /tmp/linkeddir/..non è un collegamento simbolico, ma -d /tmp/linkeddir/..è vero e quindi cd /tmp/linkeddir/..ti porta a /tmp, il cui percorso canonico è /private/tmp. Il tuo codice fa invece quello che realpath -Lfa.
Martijn Pieters

27

Un semplice one-liner in perl che sicuramente funzionerà quasi ovunque senza dipendenze esterne:

perl -MCwd -e 'print Cwd::abs_path shift' ~/non-absolute/file

Dereferenzeranno i link simbolici.

L'uso in uno script potrebbe essere così:

readlinkf(){ perl -MCwd -e 'print Cwd::abs_path shift' "$1";}
ABSPATH="$(readlinkf ./non-absolute/file)"

4
per ottenere una nuova riga finale, aggiungi -l, comeperl -MCwd -le 'print Cwd::abs_path shift' ~/non-absolute/file
pjvandehaar,

26
  1. Installa homebrew
  2. Esegui "brew install coreutils"
  3. Esegui "greadlink -f path"

greadlink è il readlink gnu che implementa -f. Puoi usare macports o anche altri, preferisco l'homebrew.


2
Non vedo alcuna differenza rispetto alla risposta fornita nel 2010 stackoverflow.com/a/4031502/695671
Jason S

Se devi usare questi comandi con i loro nomi normali, puoi aggiungere una directory "gnubin" al tuo PERCORSO dal tuo bashrc come: PATH = "/ usr / local / opt / coreutils / libexec / gnubin: $ PATH"
Denis Trofimov

20

Ho realizzato personalmente una sceneggiatura chiamata realpath che assomiglia un po 'a:

#!/usr/bin/env python
import os.sys
print os.path.realpath(sys.argv[1])

Dovresti cambiarlo in sys.argv[1]se vuoi che lo script stampi il percorso reale del primo argomento. sys.argv[0]stampa semplicemente il vero percorso dello script pyton stesso, il che non è molto utile.
Jakob Egger,

17
Ecco la versione alias, per ~/.profile:alias realpath="python -c 'import os, sys; print os.path.realpath(sys.argv[1])'"
jwhitlock

Ottima risposta, e ottimo one-liner @jwhitlock. Da quando è stato citato l'OP readlink -f, ecco una versione modificata dell'alias di @jwhitlock per supportare la possibile -fbandiera: alias realpath="python -c 'import os, sys; print os.path.realpath(sys.argv[2] if sys.argv[1] == \"-f\" else sys.argv[1])'"
codesniffer il

11

Che dire di questo?

function readlink() {
  DIR="${1%/*}"
  (cd "$DIR" && echo "$(pwd -P)")
}

Questa è l'unica soluzione che funziona per me; Ho bisogno di analizzare percorsi come ../../../->/
keflavich il

questo non può funzionare se si dispone di un collegamento simbolico, /usr/bin/ad esempio come piace al alternativessistema.
Akostadinov,

Non funziona per singoli file, solo directory. GNU readlink funziona con directory, file, symlink, ecc.
codesniffer il

7

Ecco una funzione di shell portatile che dovrebbe funzionare in QUALSIASI shell paragonabile Bourne. Risolverà la punteggiatura relativa del percorso ".. o." e dereference i collegamenti simbolici.

Se per qualche motivo non si dispone di un comando realpath (1) o readlink (1), questo può essere alias.

which realpath || alias realpath='real_path'

Godere:

real_path () {
  OIFS=$IFS
  IFS='/'
  for I in $1
  do
    # Resolve relative path punctuation.
    if [ "$I" = "." ] || [ -z "$I" ]
      then continue
    elif [ "$I" = ".." ]
      then FOO="${FOO%%/${FOO##*/}}"
           continue
      else FOO="${FOO}/${I}"
    fi

    ## Resolve symbolic links
    if [ -h "$FOO" ]
    then
    IFS=$OIFS
    set `ls -l "$FOO"`
    while shift ;
    do
      if [ "$1" = "->" ]
        then FOO=$2
             shift $#
             break
      fi
    done
    IFS='/'
    fi
  done
  IFS=$OIFS
  echo "$FOO"
}

inoltre, nel caso in cui qualcuno sia interessato qui è come implementare basename e dirname in un codice shell puro al 100%:

## http://www.opengroup.org/onlinepubs/000095399/functions/dirname.html
# the dir name excludes the least portion behind the last slash.
dir_name () {
  echo "${1%/*}"
}

## http://www.opengroup.org/onlinepubs/000095399/functions/basename.html
# the base name excludes the greatest portion in front of the last slash.
base_name () {
  echo "${1##*/}"
}

Puoi trovare la versione aggiornata di questo codice shell sul mio sito google: http://sites.google.com/site/jdisnard/realpath

EDIT: questo codice è concesso in licenza in base ai termini della licenza di 2 clausole (stile freeBSD). Una copia della licenza può essere trovata seguendo il collegamento ipertestuale sopra al mio sito.


Il real_path restituisce / nomefile invece di / percorso / in / nomefile su Mac OS X Lion nella shell bash.

@Barry Lo stesso accade su OSX Snow Leopard. Peggio ancora, le chiamate successive a real_path () concatenano l'output!
Dominique,

@Dominique: non sorprende, dal momento che l'interno della funzione non è eseguito nella sua sottostruttura ... davvero brutta e fragile.
0xC0000022L

7

FreeBSD e OSX hanno una versione statderivata da NetBSD.

È possibile regolare l'output con interruttori di formato (consultare le pagine del manuale ai collegamenti sopra).

%  cd  /service
%  ls -tal 
drwxr-xr-x 22 root wheel 27 Aug 25 10:41 ..
drwx------  3 root wheel  8 Jun 30 13:59 .s6-svscan
drwxr-xr-x  3 root wheel  5 Jun 30 13:34 .
lrwxr-xr-x  1 root wheel 30 Dec 13 2013 clockspeed-adjust -> /var/service/clockspeed-adjust
lrwxr-xr-x  1 root wheel 29 Dec 13 2013 clockspeed-speed -> /var/service/clockspeed-speed
% stat -f%R  clockspeed-adjust
/var/service/clockspeed-adjust
% stat -f%Y  clockspeed-adjust
/var/service/clockspeed-adjust

Alcune versioni di OS X di stat potrebbero non avere l' -f%Ropzione per i formati. In questo caso -stat -f%Ypotrebbe essere sufficiente. L' -f%Yopzione mostrerà la destinazione di un collegamento simbolico, mentre-f%R mostra il percorso assoluto corrispondente al file.

MODIFICARE:

Se sei in grado di utilizzare Perl (Darwin / OS X viene installato con le versioni recenti di perl) quindi:

perl -MCwd=abs_path -le 'print abs_path readlink(shift);' linkedfile.txt

funzionerà.


2
Questo è vero su FreeBSD 10, ma non su OS X 10.9.
Zanchey,

Buona pesca. La pagina del manuale delle statistiche citata è per OS / X 10.9. L' Ropzione per -f%manca. Possibilmente stat -f%Yfornisce l'output desiderato. Aggiusterò la risposta. Nota che lo statstrumento è apparso in FreeBSD nella versione 4.10 e NetBSD nella versione 1.6.
G. Cito,

Il comando Perl non funziona correttamente su 10.15. Dagli un file nella directory corrente e fornisce la directory corrente (senza il file).
codesniffer il

7

Il modo più semplice per risolvere questo problema e abilitare la funzionalità di readlink su Mac con Homebrew installato o FreeBSD è installare il pacchetto 'coreutils'. Potrebbe essere necessario anche su alcune distribuzioni Linux e altri sistemi operativi POSIX.

Ad esempio, in FreeBSD 11, ho installato invocando:

# pkg install coreutils

Su MacOS con Homebrew, il comando sarebbe:

$ brew install coreutils

Non so davvero perché le altre risposte siano così complicate, non c'è altro da dire. I file non si trovano in un posto diverso, non sono ancora installati.


7
brew install coreutils --with-default-namesper essere più specifici. Oppure finirai con "greadlink" invece ...
Henk,

Non riesco a vedere alcuna differenza nelle risposte dal 2010 stackoverflow.com/a/4031502/695671 e 2012 stackoverflow.com/a/9918368/695671 La domanda è in realtà "... su Mac e probabilmente su sistemi basati su BSD. Cosa sarebbe l'equivalente sia? " Non credo che questa sia un'ottima risposta a questa domanda. Molte ragioni, ma potresti scegliere come target macchine Mac OS senza la possibilità di installare nulla. pwd -Pè piuttosto semplice ma scompare tra molte altre risposte, comprese quelle ripetute, da cui il downvote.
Jason S,

3

Inizia l'aggiornamento

Questo è un problema così frequente che abbiamo messo insieme una libreria di Bash 4 per uso gratuito (Licenza MIT) chiamata realpath-lib . Questo è progettato per emulare readlink -f per impostazione predefinita e include due suite di test per verificare (1) che funzioni per un determinato sistema unix e (2) contro readlink -f se installato (ma non è necessario). Inoltre, può essere utilizzato per indagare, identificare e svolgere collegamenti simbolici profondi e rotti e riferimenti circolari, quindi può essere uno strumento utile per diagnosticare problemi di directory e file fisici o simbolici profondamente annidati. Può essere trovato su github.com o bitbucket.org .

Fine aggiornamento

Un'altra soluzione molto compatta ed efficiente che non si basa su niente ma Bash è:

function get_realpath() {

    [[ ! -f "$1" ]] && return 1 # failure : file does not exist.
    [[ -n "$no_symlinks" ]] && local pwdp='pwd -P' || local pwdp='pwd' # do symlinks.
    echo "$( cd "$( echo "${1%/*}" )" 2>/dev/null; $pwdp )"/"${1##*/}" # echo result.
    return 0 # success

}

Ciò include anche un'impostazione ambientale no_symlinksche consente di risolvere i collegamenti simbolici al sistema fisico. Finché no_symlinksè impostato su qualcosa, ovvero i no_symlinks='on'collegamenti simbolici saranno risolti al sistema fisico. Altrimenti verranno applicati (impostazione predefinita).

Questo dovrebbe funzionare su qualsiasi sistema che fornisce Bash e restituirà un codice di uscita compatibile con Bash a scopo di test.


3

Ci sono già molte risposte, ma nessuna ha funzionato per me ... Quindi questo è quello che sto usando ora.

readlink_f() {
  local target="$1"
  [ -f "$target" ] || return 1 #no nofile

  while [ -L "$target" ]; do
    target="$(readlink "$target")" 
  done
  echo "$(cd "$(dirname "$target")"; pwd -P)/$target"
}

1
Questo non funziona se $targetè un percorso, funziona solo se è un file.
MaxGhost

3

Un modo pigro che funziona per me,

$ brew install coreutils
$ ln -s /usr/local/bin/greadlink /usr/local/bin/readlink
$ which readlink
/usr/local/bin/readlink
/usr/bin/readlink

1

Meglio tardi che mai, suppongo. Ero motivato a svilupparlo specificamente perché i miei script Fedora non funzionavano sul Mac. Il problema è dipendenze e Bash. I Mac non li hanno, o se lo fanno, sono spesso da qualche altra parte (un altro percorso). La manipolazione del percorso di dipendenza in uno script Bash multipiattaforma nella migliore delle ipotesi è un mal di testa e un rischio per la sicurezza nella peggiore delle ipotesi, quindi è meglio evitarne l'uso, se possibile.

La funzione get_realpath () di seguito è semplice, incentrata su Bash e non sono richieste dipendenze. Uso solo l' eco e il cd predefiniti di Bash . È anche abbastanza sicuro, poiché tutto viene testato in ogni fase del percorso e restituisce condizioni di errore.

Se non vuoi seguire i collegamenti simbolici, metti set -P nella parte anteriore dello script, ma altrimenti cd dovrebbe risolvere i collegamenti simbolici di default. È stato testato con argomenti sui file che sono {absolute | parente | symlink | local} e restituisce il percorso assoluto al file. Finora non abbiamo avuto alcun problema.

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

}

Puoi combinarlo con altre funzioni get_dirname, get_filename, get_stemname e validate_path. Questi possono essere trovati nel nostro repository GitHub come realpath-lib (divulgazione completa - questo è il nostro prodotto ma lo offriamo gratuitamente alla comunità senza alcuna restrizione). Potrebbe anche servire come strumento didattico - è ben documentato.

Abbiamo fatto del nostro meglio per applicare le cosiddette pratiche "moderne di Bash", ma Bash è un argomento importante e sono certo che ci sarà sempre spazio per miglioramenti. Richiede Bash 4+ ma potrebbe funzionare con versioni precedenti se sono ancora in circolazione.


1

Dal momento che il mio lavoro è utilizzato da persone con Linux non BSD e macOS, ho optato per l'utilizzo di questi alias nei nostri script di build ( sedinclusi poiché ha problemi simili):

##
# If you're running macOS, use homebrew to install greadlink/gsed first:
#   brew install coreutils
#
# Example use:
#   # Gets the directory of the currently running script
#   dotfilesDir=$(dirname "$(globalReadlink -fm "$0")")
#   alias al='pico ${dotfilesDir}/aliases.local'
##

function globalReadlink () {
  # Use greadlink if on macOS; otherwise use normal readlink
  if [[ $OSTYPE == darwin* ]]; then
    greadlink "$@"
  else
    readlink "$@"
  fi
}

function globalSed () {
  # Use gsed if on macOS; otherwise use normal sed
  if [[ $OSTYPE == darwin* ]]; then
    gsed "$@"
  else
    sed "$@"
  fi
}

Controllo facoltativo che è possibile aggiungere per installare automaticamente le dipendenze homebrew + coreutils :

if [[ "$OSTYPE" == "darwin"* ]]; then
  # Install brew if needed
  if [ -z "$(which brew)" ]; then 
    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"; 
  fi
  # Check for coreutils
  if [ -z "$(brew ls coreutils)" ]; then
    brew install coreutils
  fi
fi

Suppongo che per essere veramente "globale" debba controllare gli altri ... ma probabilmente questo si avvicina al segno 80/20.


1

Spiegazione

coreutils è un brewpacchetto che installa utility di base GNU / Linux che corrispondono all'implementazione di Mac OSX in modo da poterle usare

Potresti trovare programmi o programmi di utilità sul tuo sistema mac osx che sembrano simili ai coreutils di Linux ("Core Utilities") ma differiscono in alcuni modi (come avere flag diversi).

Questo perché l'implementazione di Mac OSX di questi strumenti è diversa. Per ottenere il comportamento originale simile a GNU / Linux è possibile installare il coreutilspacchetto tramite il brewsistema di gestione dei pacchetti.

In questo modo verranno installate le utility core corrispondenti, precedute da g. Ad esempio readlink, troverai un greadlinkprogramma corrispondente .

Per fare in modo che readlinkfunzioni come l' implementazione GNU readlink( greadlink), puoi creare un semplice alias dopo aver installato coreutils.

Implementazione

  1. Installa birra

Segui le istruzioni su https://brew.sh/

  1. Installa il pacchetto coreutils

brew install coreutils

  1. Crea un alias

Puoi posizionare il tuo alias in ~ / .bashrc, ~ / .bash_profile o ovunque tu sia abituato a mantenere i tuoi alias bash. Personalmente tengo il mio in ~ / .bashrc

alias readlink=greadlink

Puoi creare alias simili per altri coreutil come gmv, gdu, gdf e così via. Ma attenzione che il comportamento GNU su una macchina mac potrebbe essere fonte di confusione per gli altri abituati a lavorare con coreutils nativi o potrebbe comportarsi in modo imprevisto sul sistema mac.


0

Ho scritto un'utilità realpath per OS X che può fornire gli stessi risultati di readlink -f.


Ecco un esempio:

(jalcazar@mac tmp)$ ls -l a
lrwxrwxrwx 1 jalcazar jalcazar 11  8 25 19:29 a -> /etc/passwd

(jalcazar@mac tmp)$ realpath a
/etc/passwd


Se si utilizza MacPorts, è possibile installarlo con il seguente comando: sudo port selfupdate && sudo port install realpath.


0

Veramente indipendente dalla piattaforma sarebbe anche questo R-onliner

readlink(){ RScript -e "cat(normalizePath(commandArgs(T)[1]))" "$1";}

Imitare davvero readlink -f <path> , dovrebbero essere usati $ 2 anziché $ 1.


Tranne il fatto che R non è quasi su nessun sistema per impostazione predefinita. Non ancora su Mac (a partire dalla 10.15) di cui si tratta questa domanda.
codesniffer il

0

readlink -fImplementazione conforme POSIX per script shell POSIX

https://github.com/ko1nksm/readlinkf

Questo è conforme a POSIX (senza bashismo). Non utilizza né readlinkrealpath. Ho verificato che è esattamente lo stesso confrontandolo con GNU readlink -f(vedere i risultati dei test ). Ha una gestione degli errori e buone prestazioni. Puoi tranquillamente sostituire da readlink -f. La licenza è CC0, quindi puoi usarla per qualsiasi progetto.

# POSIX compliant version
readlinkf_posix() {
  [ "${1:-}" ] || return 1
  target=$1 max_symlinks=10 CDPATH=''

  [ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
  [ -d "${target:-/}" ] && target="$target/"

  cd -P . 2>/dev/null || return 1
  while [ "$max_symlinks" -gt 0 ] && max_symlinks=$((max_symlinks - 1)); do
    if [ ! "$target" = "${target%/*}" ]; then
      cd -P "${target%/*}/" 2>/dev/null || break
      target=${target##*/}
    fi

    if [ ! -L "$target" ]; then
      target="${PWD%/}${target:+/}$target"
      printf '%s\n' "${target:-/}"
      return 0
    fi

    # `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
    #   <file mode>, <number of links>, <owner name>, <group name>,
    #   <size>, <date and time>, <pathname of link>, <contents of link>
    # https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
    link=$(ls -dl "$target" 2>/dev/null) || break
    target=${link#*" $target -> "}
  done
  return 1
}

Si prega di fare riferimento al codice più recente. Potrebbe alcuni risolto.


-2

Perl ha una funzione readlink (ad es. Come si copiano i collegamenti simbolici in Perl? ). Funziona su molte piattaforme, incluso OS X:

perl -e "print readlink '/path/to/link'"

Per esempio:

$ mkdir -p a/b/c
$ ln -s a/b/c x
$ perl -e "print readlink 'x'"
a/b/c

5
Questo è lo stesso che readlinksenza l' -fopzione - nessuna ricorsione, nessun percorso assoluto -, quindi potresti anche usare readlinkdirettamente.
mklement0

-2

La risposta di @Keith Smith fornisce un ciclo infinito.

Ecco la mia risposta, che uso solo su SunOS (a SunOS mancano così tanti comandi POSIX e GNU).

È un file di script che devi inserire in una delle tue directory $ PATH:

#!/bin/sh
! (($#)) && echo -e "ERROR: readlink <link to analyze>" 1>&2 && exit 99

link="$1"
while [ -L "$link" ]; do
  lastLink="$link"
  link=$(/bin/ls -ldq "$link")
  link="${link##* -> }"
  link=$(realpath "$link")
  [ "$link" == "$lastlink" ] && echo -e "ERROR: link loop detected on $link" 1>&2 && break
done

echo "$link"


-5

I percorsi per readlink sono diversi tra il mio sistema e il tuo. Prova a specificare il percorso completo:

/sw/sbin/readlink -f


1
E qual è - esattamente - la differenza tra / sw / sbin e / usr / bin? E perché i due binari differiscono?
troelskn,

4
Ah .. così Fink contiene un sostituto per readlinkquello compatibile con GNU . È bello sapere, ma non risolve il mio problema, poiché ho bisogno che il mio script funzioni sulla macchina di altre persone, e non posso richiedergli di installare Fink per quello.
troelskn,

Non stai rispondendo alla domanda originale. Riscrivi la tua risposta.
Jaime Hablutzel,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.