Як отримати абсолютний шлях довільного файлу з ОС X


18

Я шукаю просту команду, яку можна використовувати в Bash, щоб знайти абсолютний і кананізований шлях до файлу на ОС X (подібно до `` readlink -f '' в Linux).

Наступний зразок баш-сесії описує [вигадану] утиліту під назвою `` 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, яка робить це. Джессі Вілсон написав для цього сценарій:

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

Однак це не добре працює для шляхів безпосередньо внизу /, таких як /etc(друк //etc), а також .та ..(друк /cwd/.в обох випадках). Я спробував це змінити, але мій недостатній баш-фу не зміг мене.

Ось моя пропозиція:

#!/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
Я думаю, що ви на правильному шляху з першим підходом до оболонки, але я вважаю, що використання іншої мови для цього дещо перемагає мету, оскільки вона вводить додаткові залежності і в значній мірі еквівалентна простому складанню версії `readlink 'від GNU (1) 'під ОС X (якщо припустити, що це можна зробити; я ще не перевірив це).
Майкл Венер

2
@Michael Ви знаходитесь або в системі з GNU readlink, або в OS X - правда? У OS X є Python поза коробкою. Теоретично це нова залежність, на практиці ні. У всякому разі, це все, що я можу тобі запропонувати.
Даніель Бек

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

1
@Michael: Щоправда, але це не те, що я коментував. Я пропоную вам 90% -ве рішення, написане Джесі Вілсоном у чистому стилі + аналіз, чому це лише 90%. Якщо для вас це не проблема, це добре. Я також дав вам трохи складніше, 100% рішення в Python. Хоча інші мови сценаріїв можуть бути більш короткими (Perl ганебно так :-)), для зберігання команди потрібен додатковий файл. Або ви хочете виписувати його кожен раз, коли ви хочете його використовувати? Ось чому я додав багатолінійну здатність обробляти декілька параметрів, 1 або 2 рядки, а потім не змінювати інші.
Даніель Бек

2
@Michael, чому б 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

Я думаю, ти міг би це зробити і з пітоном, або з рубіном.

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

або зробіть це командою за допомогою

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

Мій попередній коментар щодо відповіді Даніеля Бека застосовується і тут (я б віддав перевагу суто баш-у-рішенню), хоча якщо б я вдався до використання іншої мови для досягнення цього, мені подобається ваше рішення найкраще дотепер за його стислість. :) Я, ймовірно, також завершив дзвінок до 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інакше.
Лрі

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
}

Але він не підтримує реальний шлях - відносний до. Для цього знадобиться /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.