Como posso obter o comportamento do readlink -f do GNU em um Mac?

377

No Linux, o readlinkutilitário aceita uma opção -fque segue links adicionais. Isso não parece funcionar em sistemas Mac e possivelmente baseados em BSD. Qual seria o equivalente?

Aqui estão algumas informações de depuração:

$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
Troelskn
fonte
11
Um pouco tarde, mas sua pergunta está faltando em qualquer menção ao shell que você usa. Isso é relevante, porque readlinkpode ser um comando interno ou externo.
0xC0000022L
11
Isso explicaria a diferença, talvez. Tenho certeza de que usei o bash nas duas ocasiões.
troelskn
11
Por que essa opção é ilegal em Macs?
CommaToast
3
Eu realmente desejo que a Apple faça com que o OS X suporte mais caminhos linux padrão e resolva coisas como esta. Eles poderiam fazer isso sem quebrar nada, não poderiam?
CommaToast 19/09/14
11
@CommaToast Bem, eles são enviados com Perl, para ++ :-) ... abra o Terminal.app e digite: touch myfile ; ln -s myfile otherfile ; perl -MCwd=abs_path -le 'print abs_path readlink(shift);' otherfile... no meu caso, vejo: / Users / cito / myfile`. Adicionado à minha resposta abaixo. Felicidades.
G. Cito 27/04

Respostas:

181

readlink -f faz duas coisas:

  1. Ele itera ao longo de uma sequência de links simbólicos até encontrar um arquivo real.
  2. Ele retorna o nome canônico desse arquivo - ou seja, seu nome de caminho absoluto.

Se desejar, basta criar um script de shell que use o comportamento de vanilla readlink para conseguir a mesma coisa. Aqui está um exemplo. Obviamente, você pode inserir isso em seu próprio script, onde gostaria de chamarreadlink -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

Observe que isso não inclui nenhum tratamento de erros. De particular importância, ele não detecta ciclos de vínculo simbólico. Uma maneira simples de fazer isso seria contar o número de vezes que você percorre o loop e falha se atingir um número improvávelmente grande, como 1.000.

EDITADO para usar em pwd -Pvez de$PWD .

Note que este script espera ser chamado como ./script_name filename, não -f, mude $1para $2se você quiser usar com o -f filenameGNU readlink.

Keith Smith
fonte
11
Até onde eu sei, isso não funcionará se um diretório pai do caminho for um link simbólico. Por exemplo. se foo -> /var/cux, então foo/barnão será resolvido, porque barnão é um link, embora fooseja.
troelskn
11
Ah Sim. Não é tão simples, mas você pode atualizar o script acima para lidar com isso. Vou editar (reescrever, realmente) a resposta de acordo.
213 Keith Smith
Bem, um link pode estar em qualquer lugar do caminho. Eu acho que o script poderia percorrer cada parte do caminho, mas se tornou um pouco complicado então.
31139 troelskn
11
Obrigado. O problema é que $ PWD está nos fornecendo o diretório de trabalho lógico, com base nos valores nos links simbólicos que seguimos. Podemos obter o diretório físico real com 'pwd -P'. Ele deve ser calculado perseguindo ".." até a raiz do sistema de arquivos. Vou atualizar o script na minha resposta de acordo.
Keith Smith
12
Ótima solução, mas não lida adequadamente com caminhos que precisam ser escapados , como nomes de arquivos ou diretórios com espaços incorporados . Para remediar isso, uso cd "$(dirname "$TARGET_FILE")" e TARGET_FILE=$(readlink "$TARGET_FILE")e TARGET_FILE=$(basename "$TARGET_FILE")nos lugares apropriados no código acima.
mklement0
438

MacPorts e Homebrew fornecem um pacote coreutils contendo greadlink(GNU readlink). Crédito para Michael Kallweitt post no mackb.com.

brew install coreutils

greadlink -f file.txt
tomyjwu
fonte
Se você estiver chamando o readlink de outros lugares que não o bash, outra solução seria remover / usr / bin / readlink e criar um link para / usr / local / bin / greadlink.
Chinglun #
8
Por favor, não, a menos que você a) escreva um software para o seu próprio país b) queira mexer com os outros. Escreva de tal maneira que "simplesmente funcione" para qualquer pessoa.
kgadek
14
@ching Faça isso no seu .bashrc:export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
bfontaine
11
A pergunta pede o equivalente em um sistema OS X ou BSD, isso não responde a isso. Você também terá que instalar o Homebrew. Você também instalará muitas coisas além do GNU Readlink. Se você estiver escrevendo um script para instalações padrão do OS X, esta resposta não ajudará. Você pode usar uma combinação de pwd -Pe readlinkno OS X, sem instalar nada
Jason S
4
Eu queria o GNU e outros utilitários no meu Mac que, acredito, se saíssem tão bem, sem o prefixo e facilmente acessíveis pelo usuário PATH. Fiz o que foi dito acima começando com brew install <package> --default-names. Muitas vezes, uma pergunta neste site foi respondida levemente, ou mais, fora dos requisitos técnicos do solicitante, mas dentro de uma situação semelhante e ainda tem sido muito útil. topbug.net/blog/2013/04/14/…
Pysis
43

Você pode estar interessado realpath(3)ou no Python os.path.realpath. Os dois não são exatamente iguais; a chamada da biblioteca C requer a existência de componentes do caminho intermediário, enquanto a versão do Python não.

$ 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

Eu sei que você disse que preferiria algo mais leve do que outra linguagem de script, mas, caso compile um binário seja insuportável, você pode usar Python e ctypes (disponíveis no Mac OS X 10.5) para encerrar a chamada da biblioteca:

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

Ironicamente, a versão C deste script deve ser mais curta. :)

Milhas
fonte
Sim, realpathé realmente o que eu quero. Mas parece bastante estranho que eu tenha que compilar um binário para obter essa função de um script de shell.
troelskn
4
Por que não usar o liner Python no script de shell então? (Não é tão diferente de uma chamada de linha um para readlinksi mesmo, não é?)
Telêmaco
3
python -c "import os,sys; print(os.path.realpath(os.path.expanduser(sys.argv[1])))" "${1}"trabalha com caminhos como'~/.symlink'
Wes Turner
@troelskin Eu não sabia que Perl, Python, etc. eram "permitidos" !! ... nesse caso, vou acrescentar perl -MCwd=abs_path -le 'print abs_path readlink(shift);'à minha resposta :-)
G. Cito
27

Eu odeio continuar com outra implementação, mas eu precisava de a) uma implementação de shell portátil e pura eb) cobertura de teste de unidade , pois o número de casos extremos para algo assim não é trivial .

Veja meu projeto no Github para testes e código completo. A seguir, é apresentada uma sinopse da implementação:

Como Keith Smith astuciosamente aponta, readlink -ffaz duas coisas: 1) resolve os links simbólicos de forma recursiva e 2) canoniza o resultado, portanto:

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

Primeiro, a implementação do resolvedor de link simbólico:

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 
}

Observe que esta é uma versão ligeiramente simplificada da implementação completa . A implementação completa adiciona uma pequena verificação de ciclos de link simbólico , além de massagear um pouco a saída.

Finalmente, a função para canonizar um caminho:

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")
}

É isso, mais ou menos. Simples o suficiente para colar em seu script, mas complicado o suficiente para que você seja louco por confiar em qualquer código que não tenha testes de unidade para seus casos de uso.

Michael Kropat
fonte
3
Este não é POSIX - ele usa o não-POSIX localpalavra-chave
go2null
Isso não é equivalente a readlink -f, nem de realpath. Supondo que um Mac com a fórmula coreutils instalada (portanto, com greadlinkdisponível), tente fazer isso com ln -s /tmp/linkeddir ~/Documents; greadlink -f /tmp/linkeddir/..imprime seu diretório pessoal (ele resolveu o /tmp/linkeddirlink antes de aplicar a ..dir dir pai), mas seu código produz /private/tmp/como /tmp/linkeddir/..não é um link simbólico, mas -d /tmp/linkeddir/..é verdadeiro e cd /tmp/linkeddir/..leva você a /tmpcujo caminho canônico é /private/tmp. Seu código faz o que realpath -Lfaz, em vez disso.
Martijn Pieters
27

Um one-liner simples em perl que certamente funcionará em quase todos os lugares sem dependências externas:

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

Desreferirá links simbólicos.

O uso em um script pode ser assim:

readlinkf(){ perl -MCwd -e 'print Cwd::abs_path shift' "$1";}
ABSPATH="$(readlinkf ./non-absolute/file)"
JinnKo
fonte
4
para obter uma nova linha à direita, adicionar -l, comoperl -MCwd -le 'print Cwd::abs_path shift' ~/non-absolute/file
pjvandehaar 27/05
26
  1. Instalar homebrew
  2. Execute "brew install coreutils"
  3. Execute "greadlink -f path"

O greadlink é o gnu readlink que implementa -f. Você pode usar macports ou outros também, eu prefiro homebrew.

Andrew
fonte
2
Eu não consigo ver qualquer diferença para a resposta dada em 2010 stackoverflow.com/a/4031502/695671
Jason S
Se você precisar usar esses comandos com seus nomes normais, poderá adicionar um diretório "gnubin" ao PATH a partir do seu bashrc, como: PATH = "/ usr / local / opt / coreutils / libexec / gnubin: $ PATH"
Denis Trofimov
20

Eu criei um script chamado realpath pessoalmente que se parece um pouco com:

#!/usr/bin/env python
import os.sys
print os.path.realpath(sys.argv[1])
James
fonte
Você deve alterar isso para sys.argv[1]se desejar que o script imprima o caminho real do primeiro argumento. sys.argv[0]apenas imprime o caminho real do próprio script pyton, o que não é muito útil.
Jakob Egger
17
Aqui está a versão do pseudônimo, para ~/.profile:alias realpath="python -c 'import os, sys; print os.path.realpath(sys.argv[1])'"
jwhitlock
Ótima resposta e ótimo @jwhitlock. Desde o OP citado readlink -f, aqui está uma versão modificada do alias de @jwhitlock para oferecer suporte a possíveis -fsinalizadores: alias realpath="python -c 'import os, sys; print os.path.realpath(sys.argv[2] if sys.argv[1] == \"-f\" else sys.argv[1])'"
codesniffer
11

Que tal isso?

function readlink() {
  DIR="${1%/*}"
  (cd "$DIR" && echo "$(pwd -P)")
}
Peeech
fonte
Esta é a única solução que funciona para mim; Eu preciso analisar caminhos como ../../../->/
keflavich 27/12/12
isso não funcionará se você tiver um link simbólico, /usr/bin/por exemplo, como o alternativessistema gosta de ter.
akostadinov 5/09/2013
Não funciona para arquivos individuais, apenas diretórios. O GNU readlink funciona com diretórios, arquivos, links simbólicos etc.
codesniffer
7

Aqui está uma função de shell portátil que deve funcionar em QUALQUER shell comparável da Bourne. Ele resolverá a pontuação do caminho relativo ".. ou". e links simbólicos de desreferência.

Se, por algum motivo, você não tiver um comando realpath (1) ou o readlink (1), este poderá ser um alias.

which realpath || alias realpath='real_path'

Aproveitar:

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"
}

Além disso, no caso de alguém estar interessado aqui, é como implementar o nome da base e o nome do diretório no código shell 100% puro:

## 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##*/}"
}

Você pode encontrar a versão atualizada deste código de shell no meu site do google: http://sites.google.com/site/jdisnard/realpath

EDIT: Este código está licenciado sob os termos da licença de 2 cláusulas (estilo FreeBSD). Uma cópia da licença pode ser encontrada seguindo o link acima para o meu site.

masta
fonte
O real_path retorna / filename em vez de / path / to / filename no Mac OS X Lion no shell bash.
@ Barry O mesmo acontece no OSX Snow Leopard. Pior, chamadas sucessivas para real_path () concatenam a saída!
Dominique
@ Dominique: não é surpreendente, já que o interior da função não é executado em seu próprio subshell ... feio e frágil.
0xC0000022L
7

O FreeBSD e OSX têm uma versão dostat derivada do NetBSD.

Você pode ajustar a saída com opções de formato (consulte as páginas de manual nos links acima).

%  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

Algumas versões do OS X stat podem não ter a -f%Ropção de formatos. Nesse caso, -stat -f%Ypode ser suficiente. A -f%Yopção mostrará o destino de um link simbólico, enquanto-f%R mostra o nome do caminho absoluto correspondente ao arquivo.

EDITAR:

Se você puder usar o Perl (o Darwin / OS X vem instalado com versões recentes de perl), então:

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

vai funcionar.

G. Cito
fonte
2
Isso é verdade no FreeBSD 10, mas não no OS X 10.9.
Zanchey
Boa pegada. A página de manual de estatísticas citada é para OS / X 10.9. A Ropção para -f%está ausente. Possivelmente stat -f%Yfornece a saída desejada. Eu ajustarei a resposta. Observe que a statferramenta apareceu no FreeBSD na versão 4.10 e no NetBSD na versão 1.6.
G. Cito
O comando Perl não funciona corretamente em 10.15. Dê a ele um arquivo no diretório atual e ele fornece o diretório atual (sem o arquivo).
codesniffer 21/03
7

A maneira mais fácil de resolver esse problema e ativar a funcionalidade do readlink no Mac com Homebrew instalado ou no FreeBSD é instalar o pacote 'coreutils'. Também pode ser necessário em certas distribuições Linux e em outros sistemas operacionais POSIX.

Por exemplo, no FreeBSD 11, instalei invocando:

# pkg install coreutils

No MacOS com Homebrew, o comando seria:

$ brew install coreutils

Não sei ao certo por que as outras respostas são tão complicadas, isso é tudo. Os arquivos não estão em um local diferente, eles ainda não estão instalados.

AveryFreeman
fonte
7
brew install coreutils --with-default-namespara ser mais específico. Ou você vai acabar com "greadlink" em vez ...
Henk
Não vejo diferença nas respostas de 2010 stackoverflow.com/a/4031502/695671 e 2012 stackoverflow.com/a/9918368/695671 A questão é realmente "... no Mac e possivelmente em sistemas baseados em BSD. O que seria o equivalente seja? " Não acredito que seja uma resposta muito boa para essa pergunta. Muitos motivos, mas você pode ter como alvo máquinas Mac OS sem capacidade de instalar nada. pwd -Pé bastante simples, mas desaparece entre muitas outras respostas, incluindo repetidas, daí o voto negativo.
Jason S
3

Iniciar atualização

Esse é um problema tão frequente que reunimos uma biblioteca do Bash 4 para uso gratuito (MIT License) chamada realpath-lib . Isso foi projetado para emular o readlink -f por padrão e inclui dois conjuntos de testes para verificar (1) se ele funciona para um determinado sistema unix e (2) contra o readlink -f se instalado (mas isso não é necessário). Além disso, ele pode ser usado para investigar, identificar e desabilitar links simbólicos profundos e quebrados e referências circulares, para que possa ser uma ferramenta útil para diagnosticar problemas físicos ou simbólicos e aninhados de diretórios e arquivos físicos ou profundamente aninhados. Pode ser encontrado em github.com ou bitbucket.org .

Finalizar atualização

Outra solução muito compacta e eficiente que não depende de nada além do 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

}

Isso também inclui uma configuração de ambiente no_symlinksque fornece a capacidade de resolver links simbólicos para o sistema físico. Desde que no_symlinksesteja definido como algo, ou seja,no_symlinks='on' , os links simbólicos serão resolvidos para o sistema físico. Caso contrário, eles serão aplicados (a configuração padrão).

Isso deve funcionar em qualquer sistema que forneça o Bash e retornará um código de saída compatível com o Bash para fins de teste.

AsymLabs
fonte
3

Já existem muitas respostas, mas nenhuma funcionou para mim ... Então é isso que estou usando agora.

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"
}
estani
fonte
11
Isso não funciona se $targetfor um caminho, só funciona se for um arquivo.
MaxGhost 27/03
3

Uma maneira preguiçosa que funciona para mim,

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

Antes tarde do que nunca, suponho. Eu estava motivado a desenvolver isso especificamente porque meus scripts do Fedora não estavam funcionando no Mac. O problema é dependências e Bash. Os Macs não os possuem ou, se o possuem, geralmente estão em outro lugar (outro caminho). A manipulação do caminho de dependência em um script Bash entre plataformas é, na melhor das hipóteses, uma dor de cabeça e, na pior das hipóteses, um risco de segurança - por isso, é melhor evitar o uso, se possível.

A função get_realpath () abaixo é simples, centrada no Bash e nenhuma dependência é necessária. Eu uso apenas o Bash builtins eco e cd . Também é bastante seguro, pois tudo é testado em cada estágio do caminho e retorna condições de erro.

Se você não quiser seguir os links simbólicos, coloque set -P na frente do script, mas, caso contrário, o cd deverá resolver os links simbólicos por padrão. Foi testado com argumentos de arquivo que são {absolute | parente | link simbólico | local} e retorna o caminho absoluto para o arquivo. Até agora, não tivemos problemas com isso.

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

}

Você pode combinar isso com outras funções get_dirname, get_filename, get_stemname e validate_path. Estes podem ser encontrados em nosso repositório GitHub como realpath-lib (divulgação completa - este é o nosso produto, mas o oferecemos gratuitamente à comunidade, sem restrições). Também poderia servir como uma ferramenta instrucional - está bem documentada.

Tentamos o nosso melhor para aplicar as chamadas práticas 'modernas do Bash', mas o Bash é um grande assunto e tenho certeza de que sempre haverá espaço para melhorias. Requer o Bash 4+, mas pode ser feito para funcionar com versões mais antigas, se ainda estiverem por aí.

AsymLabs
fonte
1

Como meu trabalho é usado por pessoas com Linux não BSD e com o macOS, optei por usar esses aliases em nossos scripts de construção ( sedincluídos por apresentar problemas semelhantes):

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

Verificação opcional que você pode adicionar para instalar automaticamente as dependências do 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

Suponho que, para ser verdadeiramente "global", é necessário verificar os outros ... mas isso provavelmente chega perto da marca 80/20.

clounie
fonte
1

Explicação

O coreutils é um brewpacote que instala os principais utilitários do GNU / Linux, que correspondem à implementação do Mac OSX para que você possa usá-los.

Você pode encontrar programas ou utilitários no sistema mac osx que parecem semelhantes aos coreutils do Linux ("Utilitários Principais"), mas eles diferem em alguns aspectos (como ter sinalizadores diferentes).

Isso ocorre porque a implementação do Mac OSX dessas ferramentas é diferente. Para obter o comportamento original do GNU / Linux, você pode instalar o coreutilspacote através do brewsistema de gerenciamento de pacotes.

Isso instalará os principais utilitários correspondentes, prefixados por g. Por exemplo readlink, para , você encontrará um greadlinkprograma correspondente .

Para ter um readlinkdesempenho semelhante à implementação GNU readlink( greadlink), você pode criar um alias simples depois de instalar o coreutils.

Implementação

  1. Instalar cerveja

Siga as instruções em https://brew.sh/

  1. Instale o pacote coreutils

brew install coreutils

  1. Crie um alias

Você pode colocar seu alias em ~ / .bashrc, ~ / .bash_profile ou onde quer que esteja acostumado a manter seus aliases de bash. Pessoalmente, mantenho o meu em ~ / .bashrc

alias readlink=greadlink

Você pode criar aliases semelhantes para outros coreutils, como gmv, gdu, gdf e assim por diante. Mas lembre-se de que o comportamento do GNU em uma máquina Mac pode ser confuso para outras pessoas acostumadas a trabalhar com coreutils nativos ou pode se comportar de maneiras inesperadas no seu sistema Mac.

yosefrow
fonte
0

Eu escrevi um utilitário realpath para o OS X que pode fornecer os mesmos resultados que readlink -f.


Aqui está um exemplo:

(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 você estiver usando MacPorts, você pode instalá-lo com o seguinte comando: sudo port selfupdate && sudo port install realpath.

user454322
fonte
0

Verdadeiramente independente da plataforma também seria esse R-onliner

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

Para realmente imitar readlink -f <path>, $ 2 em vez de $ 1 precisariam ser usados.

Holger Brandl
fonte
Exceto que R não está presente em nenhum sistema por padrão. Ainda não está disponível no Mac (a partir das 10.15), e é sobre isso que se trata.
codesniffer 21/03
0

readlink -fImplementação compatível com POSIX para scripts de shell POSIX

https://github.com/ko1nksm/readlinkf

Isso é compatível com POSIX (sem basismo). Não usa nem readlinknem realpath. Eu verifiquei que é exatamente o mesmo comparando com o GNU readlink -f(consulte os resultados do teste ). Possui tratamento de erros e bom desempenho. Você pode substituir com segurança a partir de readlink -f. A licença é CC0, para que você possa usá-la em qualquer projeto.

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

Por favor, consulte o código mais recente. Pode algum conserto.

Koichi Nakashima
fonte
-2

O Perl possui uma função de readlink (por exemplo, como copio links simbólicos no Perl? ). Isso funciona na maioria das plataformas, incluindo o OS X:

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

Por exemplo:

$ mkdir -p a/b/c
$ ln -s a/b/c x
$ perl -e "print readlink 'x'"
a/b/c
jonjlee
fonte
5
Isso faz o mesmo que readlinksem a -fopção - sem recursão, sem caminho absoluto -, então você pode usar readlinkdiretamente.
precisa
-2

A resposta de @Keith Smith fornece um loop infinito.

Aqui está a minha resposta, que eu uso apenas no SunOS (o SunOS perde muitos comandos POSIX e GNU).

É um arquivo de script que você deve colocar em um dos diretórios $ 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"
Carniceiro
fonte
-2

Isto é o que eu uso:

stat -f %N $your_path

Carlos Nunez
fonte
Isso não imprime o caminho completo.
Thom Wiggers
-5

Os caminhos para o readlink são diferentes entre o meu sistema e o seu. Por favor, tente especificar o caminho completo:

/sw/sbin/readlink -f

ennuikiller
fonte
11
E qual - exatamente - é a diferença entre / sw / sbin e / usr / bin? E por que os dois binários diferem?
troelskn
4
Aha .. so fink contém um substituto readlinkcompatível com o gnu. É bom saber, mas não resolve o meu problema, pois preciso que meu script seja executado na máquina de outras pessoas e não posso exigir que eles instalem o fink para isso.
troelskn
Você não está respondendo à pergunta original. Por favor, reescreva sua resposta.
Jaime Hablutzel 23/08/19