Как получить абсолютный путь к произвольному файлу из OS X


18

Я ищу простую команду, которую можно использовать в Bash, чтобы найти абсолютный и канонизированный путь к файлу в OS X (аналогично `` readlink -f'` в Linux).

В следующем примере сеанса bash описывается [вымышленная] утилита с именем `` abspath'`, которая демонстрирует желаемое поведение:

$ pwd
/Users/guyfleegman

$ ls -lR
drwxr-xr-x  4 guyfleegman  crew  136 Oct 30 02:09 foo

./foo:
-rw-r--r--  1 guyfleegman  crew  0 Oct 30 02:07 bar.txt
lrwxr-xr-x  1 guyfleegman  crew  7 Oct 30 02:09 baz.txt -> bar.txt


$ abspath .
/Users/guyfleegman

$ abspath foo
/Users/guyfleegman/foo

$ abspath ./foo/bar.txt
/Users/guyfleegman/foo/bar.txt

$ abspath foo/baz.txt
/Users/guyfleegman/foo/baz.txt

Как и в случае последнего вызова `` abspath'` в приведенном выше примере, я бы предпочел, чтобы он не разрешал автоматически символические ссылки, но я не собираюсь быть слишком требовательным.


Ответы:


16
function abspath() { pushd . > /dev/null; if [ -d "$1" ]; then cd "$1"; dirs -l +0; else cd "`dirname \"$1\"`"; cur_dir=`dirs -l +0`; if [ "$cur_dir" == "/" ]; then echo "$cur_dir`basename \"$1\"`"; else echo "$cur_dir/`basename \"$1\"`"; fi; fi; popd > /dev/null; }

Примеры:

abspath / => /

abspath /.DS_Store => /.DS_Store

abspath ~ => /Users/mschrag

cd /tmp; abspath . => /tmp

cd /; abspath .DS_Store => /.DS_Store

11

Я не думаю, что есть команда buildin, которая делает это. Джесси Уилсон написал сценарий bash для этого:

#!/bin/bash
cd -P -- "$(dirname -- "$1")" &&
printf '%s\n' "$(pwd -P)/$(basename -- "$1")"

Тем не менее, он не работает хорошо для путей непосредственно ниже /, таких как /etc(печать //etc), а также .и ..(печать /cwd/.в обоих случаях). Я попытался изменить это, но мой неэффективный bash-fu подвел меня.

Вот мое предложение:

#!/usr/bin/env python
import os.path
import sys

for arg in sys.argv[1:]:
    print os.path.abspath(arg)

Сохранить как /usr/bin/abspathили что-то подобное и сделать его исполняемым. Образец вывода:

Servus08:~ danielbeck$ abspath .
/Users/danielbeck
Servus08:~ danielbeck$ abspath /tmp
/tmp
Servus08:~ danielbeck$ abspath Documents
/Users/danielbeck/Documents
Servus08:~ danielbeck$ abspath . /tmp Documents
/Users/danielbeck
/tmp
/Users/danielbeck/Documents

Если вы делаете хотите символьной ссылки разрешение, изменить printстроку:

    print os.path.realpath(os.path.abspath(arg))

чтобы получить это:

Servus08:~ danielbeck$ abspath . /tmp Documents
/Users/danielbeck
/private/tmp
/Users/danielbeck/Documents

1
Я думаю, что вы на правильном пути с первым подходом на основе оболочки, но я полагаю, что использование другого языка для этого несколько отрицательно сказывается на цели, поскольку вводит дополнительные зависимости и в значительной степени эквивалентно простой компиляции GNU-версии readlink (1) 'под OS X (при условии, что это может быть сделано; я еще не проверил это).
Майкл Венер

2
@Michael Вы либо в системе с GNU readlink, либо в OS X - верно? OS X имеет Python из коробки. В теории это новая зависимость, на практике нет. Во всяком случае, это все, что я могу вам предложить.
Даниэль Бек

Прагматичный подход, который вы подразумеваете, заслуживает похвалы, если он слишком упрощен. В любом случае, если мы воспользуемся этим подходом вместо всего, что написано исключительно на bash (что абсолютно выполнимо и не зависит ни от чего другого - это просто невозможно сделать в одной строке), Python, вероятно, не не лучший выбор. И Perl, и Ruby (и, вероятно, PHP) могут сделать это кратко в командной строке без необходимости создания реального файла сценария.
Майкл Венер

1
@Michael: Правда, но это не то, что я комментировал. Я предлагаю вам 90% -ное решение, написанное Джесси Уилсоном в чистом виде, а также анализ того, почему оно составляет всего 90%. Если это не проблема для вас, это нормально. Я также дал вам немного более сложное, 100% решение в Python. В то время как другие языки сценариев могут быть более короткими (Perl так печально известен :-)), для всех требуется дополнительный файл для хранения команды. Или вы хотите писать это каждый раз, когда хотите использовать? По этой же причине я добавил многострочную возможность обработки нескольких параметров, 1 или 2 строки, тогда не получилось другое.
Даниэль Бек

2
@ Майкл, почему Python не будет хорошим выбором для OS X? Он даже поставляется с командами , которые на самом деле являются скрипты Python, какfile /usr/bin/xattr
Арьяна

8

Одним из вариантов будет просто установить coreutils и использовать greadlink -f. Он разрешает символические ссылки и работает с /Foo/или, ~/foo.txtесли они не существуют, но не с, /Foo/foo.txtесли /Foo/не существует.

$ brew install coreutils
$ greadlink -f /etc
/private/etc
$ greadlink -f ~/Documents/
/Users/lauri/Documents
$ greadlink -f ..
/Users
$ greadlink -f //etc/..////
/private
$ greadlink -f /Foo
/Foo
$ greadlink -f /Foo/foo.txt
$ 

Это не разрешает символические ссылки и не работает /Foo/foo.txtни с одним из них.

abspath() {
  if [ -d "$1" ]; then
    ( cd "$1"; dirs -l +0 )
  else
    ( cd "$(dirname "$1")"; d=$(dirs -l +0); echo "${d%/}/${1##*/}" )
  fi
}

abspath /etc # /etc
abspath ~/Foo/foo.txt # doesn't work
abspath ~/Foo # works
abspath .
abspath ./
abspath ../
abspath ..
abspath /
abspath ~
abspath ~/
abspath ~/Documents
abspath /\"\ \'
abspath /etc/../etc/
abspath /private//etc/
abspath /private//
abspath //private # //private
abspath ./aa.txt
abspath aa.tar.gz
abspath .aa.txt
abspath /.DS_Store
abspath ~/Documents/Books/

dirs -lвыполняет расширение тильды. dirs +0печатает только самый верхний каталог, если в стеке есть другие каталоги.


2

Я думаю, вы могли бы сделать это с помощью Python или Ruby.

$ ruby -e 'puts File.expand_path("~/somepath")'

или сделать команду с

#!/usr/bin/env ruby
puts File.expand_path(ARGV[0])

Мой предыдущий комментарий к ответу Даниэля Бека также применим и здесь (я бы предпочел чисто Bash-y решение), хотя, если бы я прибегнул к использованию другого языка, чтобы достичь этого, мне больше всего понравилось бы ваше решение из-за его краткости. :) Я бы также, вероятно, обернул вызов ruby ​​(например, функция abspath () {ruby -e "помещает File.expand_path ('$ 1')";}) и поместил его в мой `.profile '.
Майкл Венер

1

Если у вас установлен модуль File :: Spec для perl, вы можете просто сделать это:

perl -MFile::Spec -e 'print File::Spec->rel2abs("../however/complex/../you/want/to.get"), "\n"'

0

Для скриптов bash / sh вы можете использовать эту рекурсивную функцию:

canonical_readlink ()
{
    cd `dirname $1`;
    __filename=`basename $1`;
    if [ -h "$__filename" ]; then
        canonical_readlink `readlink $__filename`;
    else
        echo "`pwd -P`";
    fi
}

answer=$(canonical_readlink $0)

Некоторые переменные и подстановки команд не указаны должным образом. Вы можете использовать локальные переменные вместо имен переменных, таких как __filename. Сценарий в настоящее время ведет себя больше как в dirnameлюбом случае.
Lri

0

Установите следующую библиотеку для OSX:

brew install coreutils

greadlink -f file.txt

0

Если установка coreutils не возможна, следующие комбинации обрабатывают символические ссылки. и .. и работает с файлами и папками, как GNU realpath:

#!/usr/bin/env bash
realpath()
{
    if ! pushd $1 &> /dev/null; then 
        pushd ${1##*/} &> /dev/null
        echo $( pwd -P )/${1%/*}
    else
        pwd -P
    fi
    popd > /dev/null
}

Но он не поддерживает realpath -relative-to. Для этого потребуется /programming//a/12498485/869951 .


0

Учитывая, что ограничение - MacOS (OS X в то время), PHP доступен по умолчанию. Это вернет корневой каталог файла. Удалить, dirnameчтобы получить файл тоже.

export SOURCE_DIRECTORY="$(php -r 'echo dirname(realpath($argv[1]));' -- "${BASH_SOURCE[0]}")"
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.