Como você normaliza um caminho de arquivo no Bash?


204

Eu quero me transformar /foo/bar/..em/foo

Existe um comando bash que faz isso?


Edit: no meu caso prático, o diretório existe.


4
Importa se existe /foo/barou /foorealmente existe, ou você está interessado apenas no aspecto de manipulação de cadeias de acordo com as regras de nome de caminho?
— Chen Levy

5
@twalberg ... que é inventado meio ...
— Camilo Martin

2
@CamiloMartin não artificial em tudo - ele faz exatamente o que a pergunta pede - transforma /foo/bar/..a /foo, e usando um bashcomando. Se houver outros requisitos que não sejam reconhecidos, então talvez eles devem ser ...
— twalberg

14
@twalberg Você tem feito muito TDD -_- '
— Camilo Martin

Respostas:


193

se você deseja remover parte de um nome de arquivo do caminho, "dirname" e "basename" são seus amigos, e "realpath" também é útil.

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

realpath alternativas

Se realpathnão for suportado pelo seu shell, você pode tentar

readlink -f /path/here/.. 

Além disso

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

Funciona da mesma forma que

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

em que o caminho não precisa existir para ser normalizado.


5
Para aqueles que precisam de uma solução para o OS X, confira a resposta de Adam Liss abaixo.
— Trenton

stackoverflow.com/a/17744637/999943 Esta é uma resposta fortemente relacionada! Me deparei com as duas postagens de controle de qualidade em um dia e queria vinculá-las.
— Phyatt

realpath parece ter sido adicionado ao coreutils em 2012. Consulte o histórico do arquivo em github.com/coreutils/coreutils/commits/master/src/realpath.c .
— Noah Lavine

1
Ambos realpathe readlinkvem dos utilitários principais do GNU, então provavelmente você obtém os dois ou nenhum. Se bem me lembro, a versão readlink no Mac é um pouco diferente da versão GNU: - \
— Antoine 'hashar' Musso

100

Não sei se existe um comando direto do bash para fazer isso, mas geralmente faço

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

e funciona bem.


9
Isso normalizará, mas não resolverá os links programáveis. Isso pode ser um bug ou um recurso. :-)
— Adam Liss

4
Também tem um problema se $ CDPATH estiver definido; porque o "cd foo" mudará para qualquer diretório "foo" que seja um subdiretório de $ CDPATH, não apenas um "foo" que está no diretório atual. Eu acho que você precisa fazer algo como: CDPATH = "" cd "$ {dirToNormalize}" && pwd -P.
— MJS

7
A resposta de Tim é definitivamente a mais simples e mais portátil. CDPATH é fácil de lidar: dir = "$ (CDPATH não definido && cd" $ dir "&& pwd)"
— David Blevins

2
Isso pode ser muito perigoso ( rm -rf $normalDir) se dirToNormalize não existir!
— Frank Meulenaar

4
Sim, provavelmente é melhor usar um &&comentário do @ DavidBlevins.
— Elias Dorneles

54

Tente realpath. Abaixo está a fonte na íntegra, doada ao domínio público.

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

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

static char *s_pMyName;
void usage(void);

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


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

    if (argc < 2)
        usage();

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

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

1
Isso está incluído como parte da instalação padrão do bash? Estou ficando "comando não encontrado" em nosso sistema (bash 3.00.15 (1) no RHEL)
— Tim Whitcomb


8
É padrão no Ubuntu / BSD, não Centos / OSX
— Erik Aronesty

4
Você realmente deve encontrar a parte com "Bônus: é um comando bash e disponível em todos os lugares!" parte sobre realpath. Também é o meu favorito, mas em vez de complementar sua ferramenta da fonte. É tudo muito pesado, e na maioria das vezes você só quer readlink -f... BTW, readlinktambém NÃO é um bash embutido, mas faz parte do coreutilsUbuntu.
— Tomasz Gandor

4
Você disse que está doando isso para o domínio público, mas o cabeçalho também diz "para qualquer uso sem fins lucrativos" - você realmente pretende doá-lo para o domínio público? Isso significaria que pode ser usado para fins comerciais com fins lucrativos, bem como para organizações sem fins lucrativos ...
— me_e

36

Uma solução portátil e confiável é usar python, que é pré-instalado em praticamente todos os lugares (incluindo Darwin). Você tem duas opções:

  1. abspath retorna um caminho absoluto, mas não resolve links simbólicos:

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

  2. realpath retorna um caminho absoluto e, ao fazer isso, resolve links simbólicos, gerando um caminho canônico:

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

Em cada caso, path/to/filepode ser um caminho relativo ou absoluto.


7
Obrigado, esse foi o único que funcionou. O readlink ou o caminho real não estão disponíveis no OS X. O Python deve estar na maioria das plataformas.
— sorin

1
Só para esclarecer, readlinkestá disponível no OS X, mas não com a -fopção. Soluções portáteis discutidas aqui .
— StvnW

3
Literalmente me surpreende que essa seja a única solução sã se você não quiser seguir os links. A maneira Unix meu pé.
— precisa saber é o seguinte

34

Use o utilitário readlink do pacote coreutils.

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

1
O BSD não possui o sinalizador -f, o que significa que isso falhará mesmo no MacOS Mojave mais recente e em muitos outros sistemas. Esqueça o uso de -f se você quiser portabilidade, muitos sistemas operacionais são afetados.
— sorin

1
@sorin. A questão não é sobre Mac, é sobre Linux.
— mattalxndr

14

readlinké o padrão do bash para obter o caminho absoluto. Ele também tem a vantagem de retornar cadeias vazias se não houver caminhos ou um caminho (considerando os sinalizadores para isso).

Para obter o caminho absoluto para um diretório que pode ou não existir, mas quem são os pais, use:

abspath=$(readlink -f $path)

Para obter o caminho absoluto para um diretório que deve existir junto com todos os pais:

abspath=$(readlink -e $path)

Para canonizar o caminho fornecido e seguir os links simbólicos, se eles existirem, mas ignorar os diretórios ausentes e retornar o caminho de qualquer maneira, é:

abspath=$(readlink -m $path)

A única desvantagem é que o readlink seguirá os links. Se você não deseja seguir os links, pode usar esta convenção alternativa:

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

Isso irá chdir para a parte do diretório de $ path e imprimir o diretório atual junto com a parte do arquivo de $ path. Se não conseguir chdir, você obtém uma string vazia e um erro no stderr.


7
readlinké uma boa opção se estiver disponível. A versão do OS X não suporta as opções -eou -f. Nos três primeiros exemplos, você deve $pathcolocar aspas duplas para manipular espaços ou curingas no nome do arquivo. +1 para expansão de parâmetros, mas isso tem uma vulnerabilidade de segurança. Se pathestiver vazio, isso será direcionado cdpara o seu diretório pessoal. Você precisa de aspas duplas. abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
— toxalot

Este foi apenas um exemplo. Se você está interessado em segurança, não deve usar o bash ou qualquer outra variante do shell. Além disso, o bash tem seus próprios problemas quando se trata de compatibilidade entre plataformas, além de ter problemas com a alteração de funcionalidade entre as principais versões. O OSX é apenas uma das muitas plataformas com problemas pertinentes ao script de shell, sem mencionar que é baseado no BSD. Quando você precisa ser verdadeiramente multiplataforma, precisa estar em conformidade com o POSIX, para que a expansão dos parâmetros realmente saia da janela. Dê uma olhada no Solaris ou HP-UX algum tempo.
— Craig

6
Não significando nenhuma ofensa aqui, mas apontar questões obscuras como essa é importante. Eu só estou querendo uma resposta rápida para esse problema trivial e eu teria confiado nesse código com qualquer entrada / todas, se não fosse o comentário acima. Também é importante oferecer suporte ao OS-X nessas discussões básicas. Infelizmente, existem muitos comandos que não são suportados no OS-X, e muitos fóruns tomam isso como garantido ao discutir o Bash, o que significa que continuaremos recebendo muitos problemas de plataforma cruzada, a menos que sejam tratados mais cedo ou mais tarde.
— Rebs 5/05

13

Pergunta antiga, mas há uma maneira muito mais simples se você estiver lidando com nomes de caminhos completos no nível do shell:

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

Como o CD acontece em um subshell, não afeta o script principal.

Duas variações, supondo que os comandos internos do shell aceitem -L e -P, são:

   abspath = "$ (cd -P" $ path "&& pwd -P)" # caminho físico com links simbólicos resolvidos
   abspath = "$ (cd -L" $ path "&& pwd -L)" # caminho lógico preservando links simbólicos

Pessoalmente, raramente preciso dessa abordagem posterior, a menos que esteja fascinado com links simbólicos por algum motivo.

FYI: variação na obtenção do diretório inicial de um script que funciona mesmo que o script altere seu diretório atual posteriormente.

name0 = "$ (nome da base" $ 0 ")"; #base nome do script
dir0 = "$ (cd" $ (nome do diretório "$ 0") "&& pwd)"; #absolute começando dir

O uso do CD garante que você sempre tenha o diretório absoluto, mesmo que o script seja executado por comandos como ./script.sh que, sem o cd / pwd, geralmente fornecem apenas .. Inútil se o script gravar um CD posteriormente.


8

Como observou Adam Liss, realpathnão está incluído em todas as distribuições. O que é uma pena, porque é a melhor solução. O código fonte fornecido é ótimo e provavelmente vou começar a usá-lo agora. Aqui está o que eu tenho usado até agora, que eu compartilho aqui apenas para completar:

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

Se você deseja resolver links simbólicos, substitua pwdpor pwd -P.


Uma dica com a pwd -Popção para este caso ... Considere o que aconteceria se $(basename "$1")fosse um link simbólico para um arquivo em outro diretório. O pwd -Púnico resolve links simbólicos na parte do diretório do caminho, mas não na parte do nome da base.
— toxalot

7

Minha solução recente foi:

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

Com base na resposta de Tim Whitcomb.


Eu suspeito que isso falhe se o argumento não for um diretório. Suponha que eu queira saber para onde / usr / bin / java leva?
— Edward Falk,

1
Se você souber que é um arquivo, pushd $(dirname /usr/bin/java)tente.
— schmunk

5

Não é exatamente uma resposta, mas talvez uma pergunta de acompanhamento (a pergunta original não foi explícita):

readlinké bom se você realmente deseja seguir links simbólicos. Mas há também um caso de uso para apenas normalizando ./e ../e //seqüências, que pode ser feito puramente sintática, sem Canonicalização links simbólicos. readlinknão é bom para isso, e nem é realpath.

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

funciona para caminhos existentes, mas quebra para outros.

Um sedscript parece ser uma boa aposta, exceto que você não pode substituir iterativamente as seqüências ( /foo/bar/baz/../..-> /foo/bar/..-> /foo) sem usar algo como Perl, que não é seguro assumir em todos os sistemas, ou usar um loop feio para comparar a saída desed da sua entrada.

FWIW, uma linha usando Java (JDK 6+):

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

realpathtem uma -sopção para não resolver links simbólicos e apenas referências determinação de /./, /../e remover extras /caracteres. Quando combinado com a -mopção, realpathopera apenas no nome do arquivo e não toca em nenhum arquivo real. Ele soa como a solução perfeita. Mas, infelizmente, realpathainda está faltando em muitos sistemas.
— toxalot

A remoção de ..componentes não pode ser feita sintaticamente quando links simbólicos estão envolvidos. /one/two/../threenão é o mesmo /one/threeque twoum link simbólico para /foo/bar.
— jrw32982 suporta Monica

@ jrw32982 sim, como eu disse na minha resposta, isso é para um caso de uso em que a canonização do link simbólico não é desejada ou necessária.
— Jesse Glick #

@JesseGlick não se trata apenas de você querer ou não canonizar links simbólicos. Seu algoritmo realmente produz a resposta errada. Para que sua resposta seja correta, você precisa saber a priori que não havia links simbólicos envolvidos (ou que eles eram apenas de uma determinada forma). Sua resposta diz que você não deseja canonizá-los, não que não haja links simbólicos envolvidos no caminho.
— Jrw32982 suporta Monica

Há casos de uso em que a normalização deve ser executada sem assumir nenhuma estrutura de diretório fixa e existente. A normalização do URI é semelhante. Nesses casos, é uma limitação inerente que o resultado geralmente não esteja correto se houver links simbólicos perto de um diretório em que o resultado será aplicado posteriormente.
— perfil completo de Jesse Glick

5

Estou atrasado para a festa, mas esta é a solução que eu criei depois de ler vários tópicos como este:

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

Isso resolverá o caminho absoluto de $ 1, será agradável com ~, manterá os links simbólicos no caminho em que estão e não mexerá com sua pilha de diretórios. Retorna o caminho completo ou nada se não existir. Ele espera que $ 1 seja um diretório e provavelmente falhará, se não for, mas essa é uma verificação fácil de se fazer.


4

Falador, e resposta um pouco tarde. Preciso escrever um, pois estou preso no RHEL4 / 5 mais antigo. Lida com links absolutos e relativos e simplifica as entradas //, /./ e somedir /../.

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


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

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

3

Experimente nosso novo produto da biblioteca Bash, realpath-lib, que colocamos no GitHub para uso gratuito e sem ônus. Está completamente documentado e é uma ótima ferramenta de aprendizado.

Ele resolve caminhos locais, relativos e absolutos e não possui nenhuma dependência, exceto o Bash 4+; então deve funcionar em qualquer lugar. É grátis, limpo, simples e instrutivo.

Você pode fazer:

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

Esta função é o núcleo da biblioteca:

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

}

Ele também contém funções para get_dirname, get_filename, get_ stemname e validate_path. Experimente em várias plataformas e ajude a melhorá-lo.


2

O problema realpathé que ele não está disponível no BSD (ou no OSX). Aqui está uma receita simples extraída de um artigo bastante antigo (2009) do Linux Journal , que é bastante portátil:

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

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

Observe que essa variante também não requer que o caminho exista.


No entanto, isso não resolve os links simbólicos. O caminho real processa os caminhos que começam na raiz e seguem os links simbólicos à medida que progridem. Tudo o que isso faz é recolher as referências pai.
— Martijn Pieters

2

Com base na resposta de @ Andre, talvez eu tenha uma versão um pouco melhor, caso alguém esteja atrás de uma solução sem manipulação de loop e completamente baseada em manipulação de string. Também é útil para aqueles que não querem desferir nenhum link simbólico, que é a desvantagem de usar realpathor readlink -f.

Funciona nas versões bash 3.2.25 e superior.

shopt -s extglob

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

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

É uma boa idéia, mas perdi 20 minutos tentando fazer com que isso funcionasse em várias versões diferentes do bash. Acontece que a opção extglob shell precisa estar ativada para que isso funcione, e não é por padrão. Quando se trata da funcionalidade do bash, é importante especificar a versão necessária e as opções não padrão, porque esses detalhes podem variar entre os sistemas operacionais. Por exemplo, uma versão recente do Mac OSX (Yosemite) vem apenas com uma versão desatualizada do bash (3.2).
— Drwatsoncode

Desculpe @ricovox; Eu já atualizei isso. Estou ansioso para saber a versão exata do Bash que você tem lá. A fórmula acima (atualizada) funciona no CentOS 5.8, que vem com o bash 3.2.25
— ϹοδεMεδιϲ

Desculpe pela confusão. Este código funcionou na minha versão do Mac OSX bash (3.2.57) depois de ativar o extglob. Minha observação sobre as versões do bash era mais geral (que na verdade se aplica mais a outra resposta aqui sobre regex no bash).
— Drwatsoncode 13/10/2015

2
Agradeço sua resposta. Eu usei como base para mim. Aliás, notei vários casos em que o seu falha: (1) Caminhos relativos hello/../world(2) Pontos no nome do arquivo /hello/..world(3) Pontos após barra dupla /hello//../world(4) Ponto antes ou após barra dupla /hello//./worldou /hello/.//world (5) Pai após corrente: /hello/./../world/ (6) Pai after Parent:, /hello/../../worldetc. - Alguns desses podem ser corrigidos usando um loop para fazer correções até que o caminho pare de mudar. (Também remover dir/../, não /dir/..mas remover dir/..a partir da extremidade.)
— drwatsoncode

0

Baseado no excelente snippet python do loveborg, escrevi o seguinte:

#!/bin/sh

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

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

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

Isso funciona mesmo se o arquivo não existir. Exige que o diretório que contém o arquivo exista.


O GNU Realpath também não requer que o último elemento do caminho exista, a menos que você o userealpath -e
— Martijn Pieters

0

Eu precisava de uma solução que fizesse todos os três:

  • Trabalhe em um Mac padrão. realpathe readlink -fsão addons
  • Resolver links simbólicos
  • Tenha manipulação de erro

Nenhuma das respostas tinha os números 1 e 2. Adicionei o número 3 para salvar outras pessoas.

#!/bin/bash

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

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

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

Aqui está um breve caso de teste com alguns espaços distorcidos nos caminhos para exercitar completamente a citação

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

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

0

Eu sei que esta é uma pergunta antiga. Eu ainda estou oferecendo uma alternativa. Recentemente, encontrei o mesmo problema e não encontrei nenhum comando portátil e existente para fazer isso. Então, eu escrevi o seguinte script de shell, que inclui uma função que pode fazer o truque.

#! /bin/sh                                                                                                                                                

function normalize {
  local rc=0
  local ret

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

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

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

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

  echo "$ret"
  return $rc
}

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


0

Eu criei uma função exclusiva para lidar com isso, com foco no desempenho mais alto possível (por diversão). Ele não resolve os links simbólicos, portanto é basicamente o mesmo que realpath -sm.

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

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

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

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

Nota: Esta função não trata de caminhos iniciados com //, pois exatamente duas barras duplas no início de um caminho são um comportamento definido pela implementação. No entanto, ele lida com /,/// e assim por diante muito bem.

Essa função parece lidar com todos os casos extremos corretamente, mas ainda pode haver alguns por aí que eu não lidei.

Nota de desempenho: quando chamado com milhares de argumentos, abspathroda cerca de 10x mais lentamente que realpath -sm; quando chamado com um único argumento, abspathexecuta> 110x mais rápido do que realpath -smna minha máquina, principalmente devido à não necessidade de executar um novo programa todas as vezes.


-1

Descobri hoje que você pode usar o statcomando para resolver caminhos.

Portanto, para um diretório como "~ / Documents":

Você pode executar isso:

stat -f %N ~/Documents

Para obter o caminho completo:

/Users/me/Documents

Para links simbólicos, você pode usar a opção de formato% Y:

stat -f %Y example_symlink

O que pode retornar um resultado como:

/usr/local/sbin/example_symlink

As opções de formatação podem ser diferentes em outras versões do * NIX, mas funcionaram para mim no OSX.


1
a stat -f %N ~/Documentslinha é um arenque vermelho ... seu shell está substituindo ~/Documentspor /Users/me/Documentse statestá imprimindo seu argumento literalmente.
— danwyand

-4

Uma solução simples usando node.js:

#!/usr/bin/env node
process.stdout.write(require('path').resolve(process.argv[2]));
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.