อ้างถึงไฟล์ภายใต้ไดเรกทอรีเดียวกันของสคริปต์ที่พบใน $ PATH


33

ฉันมีไฟล์สคริปต์ทุบตีซึ่งอยู่ภายใต้ไดเรกทอรีบางส่วนที่เพิ่มไปยัง $ PATH เพื่อให้ฉันสามารถเรียกใช้สคริปต์จากไดเรกทอรีใด ๆ

มีไฟล์ข้อความอื่นภายใต้ไดเรกทอรีเดียวกันกับสคริปต์ ฉันสงสัยว่าจะอ้างถึงไฟล์ข้อความในสคริปต์ได้อย่างไร

ตัวอย่างเช่นหากสคริปต์เป็นเพียงการส่งออกเนื้อหาของไฟล์ข้อความcat textfileจะไม่ทำงานเนื่องจากเมื่อเรียกสคริปต์จากไดเรกทอรีอื่นไฟล์ข้อความจะไม่พบ


ปัญหาที่เกี่ยวข้องเมื่อเร็ว ๆ นี้มาที่นี่: รับเส้นทางของสคริปต์ปัจจุบันเมื่อดำเนินการผ่าน symlink
Caleb

คำถามนี้ตอบวิธีรับเส้นทางไฟล์สคริปต์ทุบตีอย่างน่าเชื่อถือฉันเพิ่มที่เส้นทางของฉัน: stackoverflow.com/q/4774054/1695680
ThorSummoner

คำตอบ:


24

สิ่งเหล่านี้ควรทำงานเหมือนกันตราบใดที่ไม่มี symlink (ในการขยายเส้นทางหรือสคริปต์เอง):

  • MYDIR="$(dirname "$(realpath "$0")")"

  • MYDIR="$(dirname "$(which "$0")")"

  • รุ่นสองขั้นตอนที่กล่าวมาข้างต้น:

    MYSELF="$(realpath "$0")"

    MYDIR="${MYSELF%/*}"

หากมี symlink เกี่ยวกับวิธีการสคริปต์ของคุณแล้วwhichจะให้คำตอบไม่รวมถึงความละเอียดของการเชื่อมโยงที่ ถ้าrealpathไม่ได้ติดตั้งโดยค่าเริ่มต้นในระบบของคุณคุณสามารถค้นหาได้ที่นี่

[แก้ไข]:เนื่องจากดูเหมือนว่าrealpathไม่มีข้อได้เปรียบreadlink -f จาก Caleb ที่แนะนำจึงน่าจะดีกว่าถ้าใช้หลัง การทดสอบเวลาของฉันระบุว่ามันเร็วกว่าจริง


ไม่มีปัญหา. โดยวิธีการที่ไม่ได้realpathมาจากในระบบของคุณ (สำหรับคนอื่นที่ไม่มีคุณสามารถใช้readlink -f
Caleb

@Caleb ที่จริงผมคิดว่ามันเป็นชุดของสาธารณูปโภค GNU มาตรฐาน (coreutils) แต่ฉันสามารถดูตอนนี้มันเป็นแพคเกจที่แยกต่างหาก
rozcietrzewiacz

@rozcietrzewiacz realpathวันที่กลับมาจากก่อนreadlink -f(และแม้กระทั่งreadlinkIIRC) อยู่ใน GNU coreutils (มีเครื่องมือที่คล้ายกันหลายรอบreadlink -fในที่สุดก็กลายเป็นมาตรฐานพฤตินัย); realpathถูกเก็บไว้เพื่อความเข้ากันได้กับสคริปต์ที่ยังคงใช้งานอยู่เท่านั้น
Gilles 'หยุดความชั่วร้าย'

Whats ประโยชน์จาก$(dirname "$(which "$0")")มากกว่า$(dirname $0)ที่whichไม่ได้อยู่ในปัจจุบัน? มันไม่เหมือนกันเหรอ?
UlfR

readlink -fดูเหมือนจะไม่ทำงานบน Mac OS X 10.11.6 แต่ใช้realpathงานได้ทันที
Grav

9

ระบบของฉันไม่ได้realpathเป็นข้อเสนอแนะโดย rozcietrzewiacz

คุณสามารถทำได้โดยใช้readlinkคำสั่ง ข้อได้เปรียบของการใช้การแยกวิเคราะห์whichหรือวิธีแก้ปัญหาอื่น ๆ คือแม้ว่าส่วนหนึ่งของพา ธ หรือชื่อไฟล์ที่ดำเนินการเป็น symlink คุณจะสามารถค้นหาไดเรกทอรีที่เป็นไฟล์จริงได้

MYDIR="$(dirname "$(readlink -f "$0")")"

ไฟล์ข้อความของคุณสามารถอ่านเป็นตัวแปรเช่นนี้:

TEXTFILE="$(<$MYDIR/textfile)"

@rozcietrzewiacz: จริง ๆ แล้วฉันไม่ได้อ้างถึงwhichคำแนะนำของคุณ วิธีการแก้ปัญหาปกติสำหรับเรื่องนี้เกี่ยวข้องกับเพียงdirnameหรือการรวมกันของcdและpwdใน subshell Readlink มีข้อได้เปรียบตรงนี้ realpathดูเหมือนว่าจะเป็นเพียงแค่เสื้อคลุมสำหรับreadlink -fต่อไป
Caleb

ฉันไม่รู้ว่าrealpathแตกต่างจากreadlink -fอย่างไร ฉันเห็นได้เพียงว่ามันให้ผลลัพธ์แบบเดียวกัน (ตรงข้ามกับwhich)
rozcietrzewiacz

โปรดทราบว่าreadlink -f(จาก coreutils ของ GNU) ไม่ต้องการองค์ประกอบสุดท้ายของเส้นทางที่มีอยู่readlink -eทำ แต่ไม่ได้รับการสนับสนุนbusybox readlinkซึ่งเลียนแบบพฤติกรรมของตัวเลือก-eของพวกเขา -f
dragon788

8

$0ในสคริปต์จะเป็นเส้นทางแบบเต็มไปยังสคริปต์และdirnameจะใช้เส้นทางแบบเต็มและให้ไดเรกทอรีเพียงเพื่อให้คุณสามารถทำเช่นนี้กับ cat textfile:

$ cat "$(dirname -- "$0")/textfile"

ในขณะที่สิ่งนี้ดูเหมือนว่าจะทำงานโดยไม่realpath $0ได้คุณผิดที่บอกว่า " $0ในสคริปต์จะเป็นเส้นทางที่เต็มไปยังสคริปต์"
rozcietrzewiacz

@roz ในทางใด
Michael Mrozek

1
$0../script.shเป็นคำสั่งในขณะที่มันกำลังวิ่งซึ่งสามารถเช่น
rozcietrzewiacz

ดังนั้นจริง ๆ แล้ว$(dirname "$0")ส่งคืนพา ธสัมพัทธ์ไปที่สคริปต์ซึ่งเป็นส่วนหนึ่งของคำสั่งที่เรียกใช้ - ไม่ใช่พา ธ สัมบูรณ์ สิ่งนี้สามารถนำไปสู่ปัญหาในสคริปต์ที่เปลี่ยนไดเรกทอรีในขณะที่ทำงาน
rozcietrzewiacz

@roz Ah น่าสนใจ ดังนั้นฉันคิดว่ามันจะไม่ทำให้เกิดปัญหาที่นี่เพราะเขาเรียกบางสิ่งบางอย่างบนเส้นทางโดยใช้ชื่อ แต่มันจะทำลายสิ่งอื่น ๆ ขอบคุณ
Michael Mrozek

4

คุณสามารถวางสิ่งนี้ไว้ที่ด้านบนสุดของสคริปต์ของคุณ:

cd "${BASH_SOURCE%/*}" || exit

ตัวแปรทุบตีภายใน BASH_SOURCE จริง ๆ แล้วเป็นอาร์เรย์ของชื่อพา ธ หากคุณขยายมันเป็นสตริงอย่างง่ายเช่น "$ BASH_SOURCE" คุณจะได้รับองค์ประกอบแรกซึ่งเป็นชื่อพา ธ ของฟังก์ชันหรือสคริปต์ที่ใช้งานอยู่ในปัจจุบัน

ที่มา: http://mywiki.wooledge.org/BashFAQ/028


2

ฉันกำลังลองสิ่งเหล่านี้และความเห็นจริงไม่ได้ผลสำหรับฉัน ทางออกที่ฉันไปด้วยคือ:

SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )

ซึ่งทำงานได้ดีจนถึงตอนนี้ ฉันต้องการทราบว่ามีปัญหาที่อาจเกิดขึ้นกับวิธีการนี้หรือไม่


1
ดี แต่ไม่ได้ติดตาม symlinks ลองสิ่งนี้:SCRIPT_DIR="$( cd "$(dirname "$( readlink -f ${BASH_SOURCE[0]} )")" >/dev/null 2>&1 && pwd)"
OronNavon

1

ฉันใช้:

#! /bin/sh -
dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P) || exit
dosomethingwith "${dir%/}/some-file"

ซึ่งเป็น POSIX และควรทำงานตราบเท่าที่ dirname of $0ไม่ได้อยู่ในอักขระบรรทัดใหม่ไม่ได้-และ$CDPATHไม่ได้ตั้งค่า (และอาจเป็นกรณีมุมอื่น ๆ เล็กน้อยหากสคริปต์ไม่ได้ค้นหา$PATH)


0

ฉันมักจะใช้whichเพื่อหาเส้นทางที่เต็มรูปแบบของปฏิบัติการจากเส้นทาง ตัวอย่างเช่น

which python

หากคุณรวมสิ่งนี้กับdirnameคำสั่งคุณจะได้รับ:

wp=`which python`
dn=`dirname $wp`
ls $dn
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.