วิธีดึงข้อมูลพา ธ สัมบูรณ์ของไฟล์ที่กำหนดเองจาก OS X


18

ฉันกำลังมองหาคำสั่งง่าย ๆ ที่สามารถใช้ภายใน Bash เพื่อค้นหาพา ธ สัมบูรณ์และ canonicalized ไปยังไฟล์บน OS 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 '' ในตัวอย่างข้างต้นฉันไม่ต้องการแก้ไข symlink อัตโนมัติ แต่ฉันจะไม่เลือกที่นี่มากเกินไป


คำตอบ:


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 ทำสิ่งนี้ Jesse Wilsonเขียนสคริปต์ทุบตีสำหรับสิ่งนี้:

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

ถ้าคุณทำต้องการความละเอียด symlink เปลี่ยน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) 'ภายใต้ OS X (สมมติว่าสามารถทำได้ฉันยังไม่ได้ยืนยัน)
Michael Wehner

2
@Michael คุณอยู่ในระบบด้วย GNU readlink หรือ OS X - ใช่ไหม? OS X มี Python อยู่นอกกรอบ ในทางทฤษฎีมันเป็นการพึ่งพาอาศัยกันใหม่ในทางปฏิบัติไม่ใช่ อย่างไรก็ตามมันคือทั้งหมดที่ฉันสามารถให้คุณ
Daniel Beck

วิธีการปฏิบัติที่คุณอ้างถึงนั้นน่ายกย่องถ้าง่ายเกินไป ไม่ว่าในกรณีใด ๆ ถ้าเราจะใช้วิธีนี้แทนสิ่งที่เขียนอย่างหมดจดในทุบตี (ซึ่งเป็นไปได้อย่างแน่นอนและไม่ขึ้นอยู่กับสิ่งอื่น - มันไม่สามารถทำได้ง่ายในบรรทัดเดียว) งูหลามอาจไม่ เป็นตัวเลือกที่ดีที่สุด ทั้ง Perl และ Ruby (และอาจเป็น PHP) สามารถทำสิ่งนี้ได้อย่างชัดเจนบนบรรทัดคำสั่งโดยไม่จำเป็นต้องสร้างไฟล์สคริปต์จริง
Michael Wehner

1
@Michael: จริง แต่นั่นไม่ใช่สิ่งที่ฉันแสดงความคิดเห็น ฉันเสนอโซลูชัน 90% ที่เขียนด้วย bash บริสุทธิ์โดย Jesse Wilson + การวิเคราะห์ว่าทำไมถึง 90% หากนั่นไม่ใช่ปัญหาสำหรับคุณนั่นก็ดี ฉันยังให้วิธีแก้ปัญหาที่ซับซ้อนกว่า 100% ใน Python ในขณะที่ภาษาสคริปต์อื่น ๆ อาจจะเงียบกว่า (Perl น่าอับอายดังนั้น :-)) ทุกคนต้องการไฟล์เพิ่มเติมเพื่อเก็บคำสั่ง หรือคุณต้องการที่จะเขียนมันออกมาทุกครั้งที่คุณต้องการใช้? นั่นเป็นเหตุผลที่ฉันเพิ่มความสามารถหลายบรรทัดในการจัดการหลายพารามิเตอร์ 1 หรือ 2 บรรทัดจากนั้นก็ไม่ได้ทำให้แตกต่างกัน
Daniel Beck

2
@Michael ทำไม Python จะไม่ใช่ทางเลือกที่ดีสำหรับ OS X มันยังมาพร้อมกับคำสั่งที่เป็นสคริปต์ Python เช่นfile /usr/bin/xattr
Arjan

8

greadlink -fทางเลือกหนึ่งที่จะเป็นเพียงการติดตั้งและการใช้ coreutils มันแก้ไข symlink และใช้งานได้กับ/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
$ 

สิ่งนี้ไม่ได้แก้ไข symlink และไม่สามารถใช้งานได้/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])

ความเห็นก่อนหน้านี้ของฉันเกี่ยวกับคำตอบของ Daniel Beck ก็มีผลเช่นกัน (ฉันต้องการโซลูชัน Bash-y ล้วนๆ) แม้ว่าฉันจะใช้ภาษาอื่นเพื่อให้บรรลุเป้าหมายนี้ฉันก็ชอบวิธีการแก้ปัญหาของคุณที่ดีที่สุด :) ฉันอาจจะตัดการโทรไปที่ ruby ​​(เช่น function abspath () {ruby -e "ทำให้ File.expand_path ('$ 1')";}) และใส่ลงใน `` profile 'ของฉัน
Michael Wehner

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

หากการติดตั้ง coreutils ไม่ใช่ตัวเลือกจะจัดการคอมโบของ symlink ต่อไปนี้ และ .. และทำงานกับไฟล์และโฟลเดอร์เช่นเดียวกับ 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]}")"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.