Як ви нормалізуєте шлях до файлу в Bash?


203

Я хочу перетворитися /foo/bar/..на/foo

Чи є команда bash, яка це робить?


Редагувати: в моєму практичному випадку каталог існує.


4
Чи має значення, /foo/barчи /fooіснує насправді, або вас цікавить лише аспект маніпулювання рядками відповідно до правил імені шляху?
Чен Леві

5
@twalberg ... це начебто надумано ...
Каміло Мартін

2
@CamiloMartin зовсім не надуманий - він робить саме те, що задається питанням - перетворюється /foo/bar/..на /fooта використовує bashкоманду. Якщо є інші вимоги, які не вказані, то, можливо, вони повинні бути ...
twalberg

14
@twalberg Ви робили занадто багато TDD -_- '
Каміло Мартін

Відповіді:


192

якщо ви хочете зігнати частину імені файлу з контуру, "dirname" та "basename" - ваші друзі, і "realpath" також корисний.

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 альтернативи

Якщо realpathваша оболонка не підтримується, ви можете спробувати

readlink -f /path/here/.. 

Також

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

Працює так само, як і

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

в тому, що шлях не потрібно існувати для нормалізації.


5
Для тих, хто потребує рішення OS X, ознайомтеся з відповіддю Адама Лісса нижче.
Трентон

stackoverflow.com/a/17744637/999943 Це дуже відповідна відповідь! Я за один день зіткнувся з обома цими повідомленнями щодо якості, і хотів пов’язати їх.
фіат

Здається, realpath був доданий до coreutils у 2012 році. Дивіться історію файлів на веб- сайті github.com/coreutils/coreutils/commiss/master/src/realpath.c .
Ной Лавін

1
І те, realpathі readlinkінше відбувається від основних утиліт GNU, тому, швидше за все, ви отримуєте або обидва, або жодні. Якщо я добре пам’ятаю, версія читання для Mac на версії Mac дещо відрізняється від версії GNU: - \
Antoine 'hashar' Musso

100

Я не знаю, чи є пряма команда bash для цього, але я зазвичай це роблю

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

і це добре працює.


9
Це нормалізує, але не вирішить м'які посилання. Це може бути або помилка, або функція. :-)
Адам Лісс

4
Він також має проблему, якщо визначено $ CDPATH; тому що "cd foo" перейде в будь-який каталог "foo", який є підкаталогом $ CDPATH, а не просто "foo", який знаходиться в поточному каталозі. Я думаю, вам потрібно зробити щось на кшталт: CDPATH = "" cd "$ {dirToNormalize}" && pwd -P.
mjs

7
Відповідь Тіма - це, безумовно, найпростіший і портативний. CDPATH легко розібратися з: dir = "$ (скасувати CDPATH && cd" $ dir "&& pwd)"
Девід Блевінс

2
Це може бути дуже небезпечно ( rm -rf $normalDir), якщо dirToNormalize не існує!
Френк Меуленаар

4
Так, це, мабуть, найкраще використовувати &&коментар відповідно до коментаря @ DavidBlevins.
Еліас Дорнелес

54

Спробуйте realpath. Нижче наведено джерело в повному обсязі, передане в загальнодоступне надбання.

// 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
Це включено до складу стандартної установки bash? Я отримую "команду не знайдено" в нашій системі (баш 3.00.15 (1) на RHEL)
Тім Віткомб

4
gnu.org/software/coreutils для readlink, але Realpath приходить з цього пакета: packages.debian.org/unstable/utils/realpath
Кент Фредрік

8
Це стандартно для ubuntu / BSD, а не для Centos / OSX
Erik Aronesty

4
Вам слід дійсно прокреслити частину "Bonus: це команда bash, і доступна всюди!" частина о realpath. Це також буває моїм улюбленим, але замість того, щоб складати свій інструмент із джерела. Це все у важкій вазі, і більшу частину часу ви просто хочете readlink -f... BTW - readlinkце також НЕ вбудований баш, а частина coreutilsUbuntu.
Томаш Гандор

4
Ви сказали, що даруєте це в загальнодоступне надбання, але в заголовку також написано "для будь-якого неприбуткового використання" - ви дійсно маєте намір подарувати це на публічне надбання? Це означає, що його можна використовувати як для комерційних цілей, так і для некомерційних цілей…
me_і

36

Портативне та надійне рішення - використовувати пітон, який попередньо встановлений майже скрізь (включаючи Дарвін). У вас є два варіанти:

  1. abspath повертає абсолютний шлях, але не вирішує символьні посилання:

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

  2. realpath повертає абсолютний шлях і тим самим вирішує символьні посилання, генеруючи канонічний шлях:

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

У кожному випадку це path/to/fileможе бути відносний або абсолютний шлях.


7
Дякую, це було єдиним, хто працював. readlink або realpath недоступні в ОС X. Python має бути на більшості платформ.
sorin

1
Просто для уточнення, readlinkвін доступний в OS X, тільки не з -fможливістю. Тут обговорювались портативні способи вирішення .
StvnW

3
Це буквально хизується моєю думкою, що це єдине розумне рішення, якщо ви не хочете переходити за посиланнями. Унікс, як мій ФОТО.
DannoHung

34

Використовуйте утиліту readlink з пакета coreutils.

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

1
У BSD немає прапора -f, це означає, що це не вдасться навіть на останніх MacOS Mojave та багатьох інших системах. Забудьте про використання -f, якщо ви хочете перенести, це впливає на багато ОС.
sorin

1
@sorin. Питання не в Mac, а в Linux.
mattalxndr

14

readlinkє базовим стандартом для отримання абсолютного шляху. Він також має перевагу повертати порожні рядки, якщо шляхи або шлях не існує (з огляду на прапорці).

Щоб отримати абсолютний шлях до каталогу, який може бути або не існує, але батьки якого існують, скористайтеся:

abspath=$(readlink -f $path)

Щоб отримати абсолютний шлях до каталогу, який повинен існувати разом з усіма батьками:

abspath=$(readlink -e $path)

Щоб канонізувати даний шлях і слідувати посиланнями, якщо вони трапляються, але в іншому випадку ігноруйте відсутні каталоги та просто поверніть шлях у будь-якому випадку, це:

abspath=$(readlink -m $path)

Єдиним недоліком є ​​те, що readlink буде переходити за посиланнями. Якщо ви не хочете переходити за посиланнями, ви можете скористатися цією альтернативною умовою:

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

Це дозволить chdir до частини каталогу $ path і надрукує поточну директорію разом з файловою частиною $ path. Якщо не вдасться chdir, ви отримаєте порожній рядок та помилку на stderr.


7
readlink- хороший варіант, якщо він доступний. Версія OS X не підтримує параметри -eабо -f. У перших трьох прикладах у вас повинні бути подвійні лапки, $pathщоб обробляти пробіли або символи підкреслення у файлі. +1 для розширення параметрів, але це вразливість безпеки. Якщо pathпорожній, це буде cdдо вашої домашньої директорії. Вам потрібні подвійні цитати. abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
токсалот

Це був лише приклад. Якщо ви пекло, захищені безпекою, то вам справді взагалі не слід використовувати bash чи будь-який інший варіант оболонки. Крім того, у bash є свої проблеми, коли справа стосується сумісності платформ, а також проблеми із зміною функціональності між основними версіями. OSX - лише одна з багатьох платформ з проблемами, що стосуються сценаріїв оболонок, не кажучи вже про те, що вона заснована на BSD. Коли вам належить бути справді багатоплатформою, вам потрібно відповідати POSIX, тому розширення параметрів дійсно виходить з вікна. Погляньте на Solaris або HP-UX деякий час.
Крейг

6
Тут не мається на увазі жодних правопорушень, але важливо вказати на такі незрозумілі питання, як це. Я просто хочу швидкої відповіді на цю тривіальну проблему, і я б довірив цьому коду будь-який / все введення, якби не коментар вище. Також важливо підтримувати OS-X в цих дискусіях. Існує багато команд, які, на жаль, не підтримуються в OS-X, і багато форумів сприймають це як належне під час обговорення Bash, що означає, що ми продовжуватимемо багато питань, пов’язаних з платформою, якщо це не буде вирішено швидше, ніж пізніше.
Ребс

13

Старе питання, але є набагато простіший спосіб, якщо ви маєте справу з повними іменами шляху на рівні оболонки:

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

Оскільки компакт-диск трапляється в нижній частині, він не впливає на основний сценарій.

Дві варіанти, припускаючи, що ваші вбудовані команди оболонки приймають -L та -P, це:

   abspath = "$ (cd -P" $ path "&& pwd -P)" #фізичний шлях з вирішеними символьними посиланнями
   abspath = "$ (cd -L" $ path "&& pwd -L)" #логічний шлях із збереженням посилань

Особисто мені рідко потрібен такий пізніший підхід, якщо я чомусь не захоплююсь символічними посиланнями.

FYI: варіація отримання початкового каталогу скрипту, який працює навіть у тому випадку, якщо сценарій згодом змінює поточний каталог.

name0 = "$ (базове ім'я" $ 0 ")"; # base ім'я сценарію
dir0 = "$ (cd" $ (dirname "$ 0") "&& pwd)"; #absolute стартовий реж

Використання CD запевняє, що у вас завжди є абсолютна директорія, навіть якщо сценарій працює за допомогою таких команд, як ./script.sh, який без cd / pwd часто дає просто .. Марно, якщо пізніше сценарій має компакт-диск.


8

Як зазначав Адам Лісс realpath, не в комплекті з кожним розповсюдженням. Що соромно, бо це найкраще рішення. Наданий вихідний код чудовий, і я, мабуть, почну його використовувати вже зараз. Ось те, що я використовував дотепер, яким я поділяюсь тут лише для повноти:

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

Якщо ви хочете, щоб воно вирішило посилання, просто замініть pwdна pwd -P.


Один готч з pwd -Pможливістю для цього випадку ... Поміркуйте, що буде, якби $(basename "$1")було посилання на файл в іншому каталозі. pwd -PТільки дозволяє символічні посилання в частині каталогу шляху, але не частина BASENAME.
токсалот

7

Моє недавнє рішення:

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

На основі відповіді Тіма Віткомба.


Я підозрюю, що це не вдається, якщо аргумент не є каталогом. Припустимо, я хочу знати, куди / usr / bin / java веде?
Едвард Фолк

1
Якщо ви знаєте, що це файл, ви можете pushd $(dirname /usr/bin/java)спробувати.
шмонк

5

Не зовсім відповідь, але, можливо, наступне запитання (оригінальне запитання не було явним):

readlinkдобре, якщо ви насправді хочете слідувати посиланнями. Але є також варіант використання тільки нормалізують ./і ../та //послідовностей, які можуть бути зроблені чисто синтаксично, без канонізації симлінк. readlinkце не добре для цього, і ні realpath.

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

працює для існуючих шляхів, але перерви для інших.

sedСценарій , здавалося б хорошим вибором, за винятком того, що ви не можете итеративно замінити послідовності ( /foo/bar/baz/../..-> /foo/bar/..-> /foo) , не використовуючи що - щось на зразок Perl, який не є безпечним припустити на всіх системах, або з допомогою якої - то потворний цикл , щоб порівняти вихід sedдо його вхід.

FWIW, однолінійний з використанням 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

realpathмає -sможливість не дозволити символічні посилання і тільки вирішувати посилання /./, /../і видалити зайві /символи. У поєднанні з -mопцією realpathпрацює лише на ім'я файлу та не торкається жодного фактичного файлу. Це звучить як ідеальне рішення. Але, на жаль, realpathвсе ще бракує багатьох систем.
токсалот

Видалення ..компонентів не може бути синтаксично, якщо задіяні символьні посилання. /one/two/../threeне є тим самим, як /one/threeніби twoє символьним посиланням на /foo/bar.
jrw32982 підтримує Моніку

@ jrw32982 так, як я вже говорив у своїй відповіді, це для випадків використання, коли кананізація symlink не потрібна чи потрібна.
Джессі Глік

@JesseGlick - це не лише випадок того, чи хочете ви канонізувати символьні посилання чи ні. Ваш алгоритм насправді дає неправильну відповідь. Щоб ваша відповідь була правильною, ви повинні апріорі знати, що жодних символьних посилань не було (або що вони були лише певної форми). Ваша відповідь говорить, що ви не хочете канонізувати їх, не те, що немає ніяких символьних посилань, що беруть участь у шляху.
jrw32982 підтримує Моніку

Є випадки використання, коли нормалізація повинна виконуватися, не допускаючи жодної фіксованої, існуючої структури каталогів. Нормалізація URI схожа. У цих випадках є властивим обмеженням, що результат, як правило, не буде правильним, якщо трапляються символьні посилання біля каталогу, де результат буде застосовано пізніше.
Джессі Глік

5

Я спізнююсь на вечірку, але це рішення, яке я підробив, прочитавши купу ниток на кшталт цього:

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

Це дозволить вирішити абсолютний шлях у $ 1, грати добре з ~, зберігати посилання на шляху, де вони є, і це не буде возитися зі стеком вашого каталогу. Він повертає повний шлях або нічого, якщо його не існує. Він очікує, що $ 1 буде каталогом і, ймовірно, не вдасться, якщо це не так, але це легко перевірити, як зробити це самостійно.


4

Говорена та трохи пізня відповідь. Мені потрібно написати один, оскільки я застряг на старшому RHEL4 / 5. Я обробляє абсолютні та відносні посилання та спрощує записи //, /./ та 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

Спробуйте наш новий продукт із бібліотекою Bash realpath-lib, який ми розмістили на GitHub для вільного та необмеженого використання. Це ретельно задокументовано і робить чудовий інструмент навчання.

Він вирішує локальні, відносні та абсолютні шляхи і не має ніяких залежностей, окрім Bash 4+; тому воно має працювати майже де завгодно. Це безкоштовно, чисто, просто та повчально.

Ви можете зробити:

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

Ця функція є ядром бібліотеки:

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

}

Він також містить функції get_dirname, get_filename, get_ ім'я стовбура та validate_path. Спробуйте це на різних платформах та допоможете вдосконалити його.


2

Проблема realpathполягає в тому, що він не доступний на BSD (або OSX для цього питання). Ось простий рецепт, витягнутий із досить старої (2009 р.) Статті з журналу Linux , який є досить портативним:

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

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

Зауважте, що цей варіант також не вимагає існування шляху.


Однак це не вирішує символьні посилання. Realpath обробляє шляхи, починаючи від кореня і слідуючи посиланнями по мірі просування. Все це - згортання батьківських посилань.
Martijn Pieters

2

Виходячи з відповіді @ Andre, я можу мати трохи кращу версію, якщо хтось знайде рішення, що не має циклу, повністю на основі рядкових маніпуляцій. Це також корисно для тих, хто не хоче скинути з ладу жодні символьні посилання, що є недоліком використання realpathабо readlink -f.

Він працює на bash версії 3.2.25 і новіших.

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

Це гарна ідея, але я витратив 20 хвилин, намагаючись змусити це працювати над різними версіями баш. Виявляється, для роботи цього параметра потрібно включити опцію оболонки extglob, і це не за замовчуванням. Що стосується функціональних можливостей bash, важливо вказати як необхідну версію, так і параметри, що не використовуються за замовчуванням, оскільки ці деталі можуть відрізнятися між ОС. Наприклад, остання версія Mac OSX (Yosemite) поставляється лише із застарілою версією bash (3.2).
drwatsoncode

Вибачте @ricovox; Зараз я їх оновив. Я нетерплячий дізнатися точну версію Bash у вас там. Вищенаведена формула (оновлена) працює на CentOS 5.8, який поставляється з bash 3.2.25
MοδεMεδιϲ

Вибачте за непорозуміння. Цей код DID працює на моїй версії Mac OSX bash (3.2.57), коли я ввімкнув extglob. Моє зауваження щодо версій bash було більш загальним (що насправді більше стосується іншої відповіді, що стосується регулярного вираження в bash).
drwatsoncode

2
Я вдячний за вашу відповідь. Я використовував це як основу для себе. Між іншим, я помітив декілька випадків, коли у вас не виходить: (1) Відносні шляхи hello/../world(2) Точки у назві файлу /hello/..world(3) Точки після подвійного косого кута /hello//../world(4) Точка до або після подвійної косої коси /hello//./worldабо /hello/.//world (5) Батько після поточного: /hello/./../world/ (6) Батько після Parent: /hello/../../worldі т. д. - Деякі з них можна виправити, використовуючи цикл для внесення виправлень, поки шлях не перестане змінюватися. (Також видаліть dir/../, /dir/..але не видаліть dir/..з кінця.)
drwatsoncode

0

Спираючись на чудовий фрагмент python loveborg, я написав це:

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

Це працює, навіть якщо файл не існує. Для цього потрібен каталог, що містить файл.


GNU Realpath не вимагає існування останнього елемента на шляху, якщо ви не використовуєтеrealpath -e
Martijn Pieters

0

Мені потрібно було рішення, яке би виконало всі три:

  • Робота на складі Mac. realpathі readlink -fє аддон
  • Розв’яжіть символьні посилання
  • Майте обробку помилок

Жодна з відповідей не мала і №1, і №2. Я додав №3, щоб врятувати інших від подальшого гоління як.

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

Ось короткий тестовий випадок з деякими закрученими пробілами в шляхах для повного здійснення цитування

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

Я знаю, що це давнє питання. Я все ще пропоную альтернативу. Нещодавно я зустрічався з тією ж проблемою і не знайшов жодної існуючої та портативної команди для цього. Тому я написав наступний скрипт оболонки, який включає функцію, яка може виконати трюк.

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

Я створив лише вбудовану функцію для вирішення цього питання з акцентом на максимально високу продуктивність (для розваги). Він не вирішує символьні посилання, тому він в основному такий же, як 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;
}

Примітка. Ця функція не обробляє контури, починаючи з того //, що саме два подвійних косої риски на початку шляху є поведінкою. Однак він справляється /,/// і так далі просто відмінно.

Ця функція, здається, справляється з усіма крайовими справами належним чином, але там все ще можуть бути деякі, з якими я не займався.

Примітка про ефективність: коли викликається з тисячами аргументів, abspathпрацює приблизно в 10 разів повільніше realpath -sm; коли викликається одним аргументом, abspathпрацює> 110x швидше, ніж realpath -smна моїй машині, в основному через те, що не потрібно кожного разу виконувати нову програму.


-1

Сьогодні я виявив, що ви можете використовувати stat команду для вирішення шляхів.

Отже, для каталогу типу "~ / Documents":

Ви можете запустити це:

stat -f %N ~/Documents

Щоб отримати повний шлях:

/Users/me/Documents

Для посилань ви можете скористатися параметром формату% Y:

stat -f %Y example_symlink

Що може повернути такий результат, як:

/usr/local/sbin/example_symlink

Параметри форматування можуть бути різними в інших версіях * NIX, але вони працювали для мене на OSX.


1
stat -f %N ~/Documentsлінія червона оселедець ... ваша оболонка міняє ~/Documentsз /Users/me/Documents, а statпросто друкувати його аргумент дослівно.
danwyand

-4

Просте рішення з використанням node.js:

#!/usr/bin/env node
process.stdout.write(require('path').resolve(process.argv[2]));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.