Cách truy xuất đường dẫn tuyệt đối của tệp tùy ý từ OS X


18

Tôi đang tìm kiếm một lệnh đơn giản có thể được sử dụng trong Bash để tìm đường dẫn tuyệt đối và được chuẩn hóa đến một tệp trên OS X (tương tự như `` readlink -f'` trong Linux).

Phiên bash mẫu sau đây mô tả một tiện ích [hư cấu] có tên là `` abspath'` thể hiện hành vi mong muốn:

$ 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

Như với lời gọi cuối cùng của `` abspath'` trong ví dụ trên, tôi thích nó không tự động giải quyết các liên kết tượng trưng, ​​nhưng tôi sẽ không quá kén chọn ở đây.


Câu trả lời:


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

Ví dụ:

abspath / => /

abspath /.DS_Store => /.DS_Store

abspath ~ => /Users/mschrag

cd /tmp; abspath . => /tmp

cd /; abspath .DS_Store => /.DS_Store

11

Tôi không nghĩ rằng có một lệnh buildin thực hiện điều này. Jesse Wilson đã viết một kịch bản bash cho điều này:

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

Tuy nhiên, nó không hoạt động tốt cho các đường dẫn ngay bên dưới /, chẳng hạn như /etc(in //etc), cũng như ...(in /cwd/.trong cả hai trường hợp). Tôi đã cố gắng sửa đổi nó, nhưng bash-fu không hiệu quả của tôi đã làm tôi thất bại.

Đây là gợi ý của tôi:

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

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

Lưu dưới dạng /usr/bin/abspathhoặc một cái gì đó tương tự và làm cho nó thực thi được. Đầu ra mẫu:

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

Nếu bạn làm muốn độ phân giải liên kết tượng trưng, thay đổi printdòng như thế này:

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

để có được điều này:

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

1
Tôi nghĩ rằng bạn đang đi đúng hướng với cách tiếp cận dựa trên vỏ đầu tiên, nhưng tôi tin rằng việc sử dụng một ngôn ngữ khác để thực hiện điều này phần nào đánh bại mục đích, vì nó giới thiệu các phụ thuộc bổ sung và khá giống với việc biên dịch phiên bản `readlink của GNU (1) 'trong OS X (giả sử điều này có thể được thực hiện; tôi chưa xác minh nó).
Michael Wehner

2
@Michael Bạn đang ở trên một hệ thống có liên kết đọc GNU hoặc trên OS X - phải không? OS X có Python ra khỏi hộp. Về lý thuyết, đó là một sự phụ thuộc mới, trong thực tế thì không. Dù sao, đó là tất cả những gì tôi có thể cung cấp cho bạn.
Daniel Beck

Cách tiếp cận thực dụng mà bạn ngụ ý là đáng khen ngợi, nếu quá đơn giản. Trong mọi trường hợp, nếu chúng ta thực hiện phương pháp này thay cho bất kỳ điều gì được viết hoàn toàn bằng bash (điều này hoàn toàn có thể thực hiện được và không phụ thuộc vào bất cứ điều gì khác - nó chỉ có thể dễ dàng được thực hiện trong một dòng), Python có thể không Đó là sự lựa chọn tốt nhất. Cả Perl và Ruby (và có lẽ PHP) đều có thể thực hiện việc này một cách cô đọng trên dòng lệnh mà không cần phải tạo tệp tập lệnh thực tế.
Michael Wehner

1
@Michael: Đúng, nhưng đó không phải là những gì tôi đã nhận xét. Tôi cung cấp cho bạn một giải pháp 90% được viết bằng bash thuần túy của Jesse Wilson + phân tích tại sao nó chỉ 90%. Nếu đó không phải là vấn đề với bạn, thì tốt thôi. Tôi cũng đã cung cấp cho bạn một giải pháp 100% phức tạp hơn một chút trong Python. Mặc dù các ngôn ngữ kịch bản lệnh khác có thể dễ hiểu hơn (Perl khét tiếng như vậy :-)), tất cả đều yêu cầu một tệp bổ sung để lưu trữ lệnh. Hay bạn muốn viết nó ra mỗi khi bạn muốn sử dụng nó? Đó cũng là lý do tại sao tôi đã thêm khả năng đa dòng để xử lý nhiều tham số, 1 hoặc 2 dòng sau đó không tạo sự khác biệt.
Daniel Beck

2
@Michael, tại sao Python không phải là một lựa chọn tốt trên OS X? Nó thậm chí còn vận chuyển với các lệnh thực sự là các tập lệnh Python, nhưfile /usr/bin/xattr
Arjan

8

Một lựa chọn sẽ là chỉ cài đặt coreutils và sử dụng greadlink -f. Nó giải quyết các liên kết tượng trưng và nó hoạt động với /Foo/hoặc ~/foo.txtnếu chúng không tồn tại, nhưng không tồn tại /Foo/foo.txtnếu /Foo/không tồn tại.

$ 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
$ 

Điều này không giải quyết được các liên kết tượng trưng và nó cũng không hoạt động /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 -lthực hiện mở rộng dấu ngã. dirs +0chỉ in thư mục trên cùng nếu có các thư mục khác trong ngăn xếp.


2

Tôi đoán bạn có thể làm điều đó với cả trăn hoặc ruby.

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

hoặc biến nó thành một lệnh với

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

Nhận xét trước đây của tôi về câu trả lời của Daniel Beck cũng áp dụng ở đây (tôi thích một giải pháp thuần túy của Bash-y), mặc dù nếu tôi phải sử dụng một ngôn ngữ khác để đạt được điều này, tôi thích giải pháp của bạn nhất cho đến nay vì sự ngắn gọn của nó. :) Tôi cũng có thể gói lời gọi tới ruby ​​(ví dụ: hàm abspath () {ruby -e "đặt File.Exand_path ('$ 1')";}) và đặt nó vào `.profile 'của tôi.
Michael Wehner

1

Nếu bạn đã cài đặt mô-đun File :: Spec cho perl, bạn có thể thực hiện việc này:

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

0

Đối với tập lệnh bash / sh, bạn có thể sử dụng hàm đệ quy này:

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)

Một số biến và lệnh thay thế không được trích dẫn đúng. Bạn có thể sử dụng biến cục bộ thay vì tên biến như __filename. Kịch bản hiện hành xử giống như dirnamedù sao đi nữa.
Lri

0

Cài đặt thư viện sau cho OSX:

brew install coreutils

greadlink -f file.txt

0

Nếu cài đặt coreutils không phải là một tùy chọn, phần sau đây sẽ xử lý các combo liên kết tượng trưng ,. và .. và hoạt động trên các tệp và thư mục như GNU realpath không:

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

Nhưng nó không hỗ trợ - liên quan đến. Điều này sẽ yêu cầu /programming//a/12498485/869951 .


0

Cho rằng ràng buộc là MacOS (OS X tại thời điểm đó), PHP có sẵn theo mặc định. Điều này sẽ trả về thư mục gốc của tập tin. Hủy bỏ dirnameđể có được các tập tin, quá.

export SOURCE_DIRECTORY="$(php -r 'echo dirname(realpath($argv[1]));' -- "${BASH_SOURCE[0]}")"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.