स्क्रिप्ट के निरपेक्ष मार्ग को प्राप्त करने का पोर्टेबल तरीका?


29

(निरस्त) स्क्रिप्ट के लिए एक पोर्टेबल तरीका क्या है जो इसका पूर्ण मार्ग निर्धारित करता है?

लिनक्स पर मैं कुछ का उपयोग करता हूँ

mypath=$(readlink -f $0)

... लेकिन यह पोर्टेबल नहीं है। (उदाहरण के लिए, readlinkडार्विन -fध्वज को नहीं पहचानता है , और न ही इसके समकक्ष है।) (इसके अलावा, इसके readlinkलिए उपयोग करना, माना जाता है, एक सुंदर अस्पष्ट-सी दिखने वाली हैक है।)

अधिक पोर्टेबल तरीका क्या है?


जवाबों:


28

इसके साथ zsh, यह सिर्फ:

mypath=$0:A

अब अन्य गोले के लिए, हालांकि realpath()और readlink()मानक कार्य हैं (बाद वाला एक सिस्टम कॉल है), realpathऔर readlinkमानक कमांड नहीं हैं, हालांकि कुछ सिस्टम में एक या दूसरे या दोनों विभिन्न व्यवहार और सुविधा सेट के साथ हैं।

पोर्टेबिलिटी के लिए, आप इसका सहारा लेना चाहते हैं perl:

abs_path() {
  perl -MCwd -le '
    for (@ARGV) {
      if ($p = Cwd::abs_path $_) {
        print $p;
      } else {
        warn "abs_path: $_: $!\n";
        $ret = 1;
      }
    }
    exit $ret' "$@"
}

यही कारण है कि अधिक जीएनयू की तरह व्यवहार करते हैं जाएगा readlink -fसे realpath()(जीएनयू readlink -e) में है कि यह करता है, तो फ़ाइल के रूप में लंबे समय के रूप में अपनी dirname करता है मौजूद नहीं है शिकायत नहीं होंगे।


नोट: यह आपके लिए काम नहीं करता है .zshrc: इस पोस्ट को इसके बजाय देखें ।
ब्रायस गुइंटा

24

Zsh में आप निम्न कार्य कर सकते हैं:

mypath=${0:a}

या, वह निर्देशिका प्राप्त करने के लिए जिसमें स्क्रिप्ट रहती है:

mydir=${0:a:h}

स्रोत: zshexpn (1) मैन पेज, अनुभाग इतिहास विस्तार, उपधारा संशोधक (या बस info -f zsh -n Modifiers)।


मिठाई! मैं उम्र के लिए इस तरह somethink की तलाश कर रहा हूं और इसके लिए खोज करने वाले zsh manpages के पूरे समूह को पढ़ा है, लेकिन यह मेरे लिए 'इतिहास विस्तार' के तहत देखने के लिए कभी नहीं हुआ होगा।
वुकर टिम्नरक्रूल

1
ग्नू के बराबर readlink -fबल्कि होगा $0:A
स्टीफन चेज़लस

11

मैं कई वर्षों से इसका उपयोग कर रहा हूं:

# The absolute, canonical ( no ".." ) path to this script
canonical=$(cd -P -- "$(dirname -- "$0")" && printf '%s\n' "$(pwd -P)/$(basename -- "$0")")

1
मुझें यह पसंद है! अब तक, यह बहुत पोर्टेबल है। विंडोज 2008 पर सोलारिस, ओमनीओएस, लिनक्स, मैक और यहां तक ​​कि सिगविन पर काम करता है।
टिम कैनेडी

4
जहां यह GNU के समतुल्य नहीं readlink -fहै, जब स्क्रिप्ट स्वयं एक सिम्लिंक है।
स्टीफन चेजलस

उबंटू 16.04 के साथ zsh, अगर मैं इसे सीधे या उपधारा में निष्पादित करता हूं (जैसा कि सुझाव दिया गया है) जबकि मेरे घर के डायर ( /home/ville) में, यह प्रिंट करता है /home/ville/zsh
विले

8

इस वाक्य रचना (के साथ परीक्षण किसी भी बॉर्न शैल शैली दुभाषिया के लिए पोर्टेबल होना चाहिए bash, ksh88, ksh93, zsh, mksh, dashऔर busybox sh):

mypath=$(exec 2>/dev/null;cd -- $(dirname "$0"); unset PWD; /usr/bin/pwd || /bin/pwd || pwd)
echo mypath=$mypath

यह संस्करण विरासत एटी एंड टी बॉर्न शेल (गैर पॉसिक्स) के लिए संगतता जोड़ता है:

mypath=`dirname "$0"`
mypath=`exec 2>/dev/null;(cd -- "$mypath") && cd -- "$mypath"|| cd "$mypath"; unset PWD; /usr/bin/pwd || /bin/pwd || pwd`
echo mypath=$mypath

धन्यवाद। हालांकि, मुझे लगता है कि परेशान $PWDहोना ओवरकिल हो सकता है - आप इसे पूर्ण वर्तमान की तरह सेट कर सकते हैं cd -P .। मुझे संदेह है कि बुर्जहेल में काम करेगा - लेकिन यह उन सभी में काम करना चाहिए जिन्हें आपने पहले परीक्षण किया था। मेरे लिए वैसे भी है।
mikeserv

@mo आप किस OS पर चल रहे हैं?
19

कौन है? क्या?
mikeserv

@mikeserv मूस एक राहगीर है जिसने किसी मुद्दे के बारे में एक टिप्पणी पोस्ट की है zshऔर dirnameजल्दी से अपनी टिप्पणी वापस ले ली है ...
jlliagre

आपकी बॉर्न शेल स्क्रिप्ट बॉर्न शेल के साथ काम नहीं करेगी क्योंकि बॉर्न शेल सीडी (1) के लिए गेटटॉप () का उपयोग नहीं करता है।
विद्वान

4

आप वास्तव में पूर्ण पथ, यानी मूल निर्देशिका से एक पथ मान लिया गया:

case $0 in
  /*) mypath=$0;;
  *) mypath=$PWD/$0;;
esac

यह किसी भी बॉर्न-शैली के शेल में काम करता है, वैसे।

यदि आप सभी प्रतीकात्मक लिंक के साथ एक मार्ग को हल करते हैं, तो यह अलग बात है। readlink -fलिनक्स पर काम करता है (कुछ स्ट्राइप्ड-डाउन बिजीबॉक्स सिस्टम को छोड़कर), FreeBSD, NetBSD, OpenBSD और Cygwin, लेकिन OS / X, AIX, HP / UX या Solaris पर नहीं। यदि आपके पास है readlink, तो आप इसे लूप में कह सकते हैं:

realpath () {
  [ -e "$1" ] || return
  case $1 in
    /*) :;;
    *) set "$PWD/$1";;
  esac
  while [ -L "$1" ]; do
    set "${1%/*}" "$(readlink "$1")"
    case $2 in
      /*) set "$2";;
      *) if [ -z "$1" ]; then set "/$2"; else set "$(cd "$1" && pwd -P)/$2"; fi;;
    esac
  done
  case $1 in
    */.|*/..) set "$(cd "$1" && pwd -P)";;
    */./*|*/../*) set "$(cd "${1%/*}" && pwd -P)/${1##*/}"
  esac
  realpath=$1
}

यदि आपके पास नहीं है readlink, तो आप इसके साथ अनुमानित कर सकते हैं ls -n, लेकिन यह केवल तभी काम करता है जब lsफ़ाइल नाम में किसी भी गैर-मुद्रण योग्य वर्ण को न जोड़ा जाए।

poor_mans_readlink () {
  if [ -L "$1" ]; then
    set -- "$1" "$(LC_ALL=C command ls -n -- "$2"; echo z)"
    set -- "${2%??}"
    set -- "${2#*"$1 -> "}"
  fi
  printf '%s\n' "$1"
}

(अतिरिक्त zमामले में लिंक लक्ष्य एक नई पंक्ति में समाप्त होता है, जो कमांड प्रतिस्थापन अन्यथा अन्यथा खा जाएगा। realpathफ़ंक्शन उस मामले को निर्देशिका नामों के लिए संभाल नहीं करता है, वैसे)।


1
क्या आप किसी ऐसे lsकार्यान्वयन के बारे में जानते हैं जो गैर-मुद्रण योग्य वर्णों का प्रबंधन करता है जब आउटपुट टर्मिनल पर नहीं जाता है।
स्टीफन चेज़लस

1
@ स्टीफनचैलेजेलस touch Stéphane; LC_ALL=C busybox ls Stéphane | catSt??phane(यदि यह नाम UTF-8 में है, तो लैटिन 1 आपको सिंगल देता है ?)। मुझे लगता है कि मैंने पुराने वाणिज्यिक यूनियनों पर भी देखा है।
गाइल्स का SO- बुराई से रोकना '

@ स्टीफनचेज़लस मैंने कई बग तय किए हैं लेकिन बड़े पैमाने पर परीक्षण नहीं किए गए हैं। मुझे बताएं कि क्या यह अभी भी कुछ मामलों में विफल रहता है (कुछ निर्देशिका में निष्पादन अनुमतियों की कमी के अलावा, मैं उस किनारे के मामले से निपटने नहीं जा रहा हूं)।
गिल्स एसओ- बुराई को रोकना '

@ गिल्स - busyboxयह क्या है? अनुसार git busybox ls में 2011 के बाद से कोई कोड परिवर्तन नहीं हुआ है। मेरा busybox ls- लगभग 2013 - यह बात नहीं करता है। यह एक - 2012 2012 - करता हैयह क्यों समझा सकता है। क्या आपने busyboxयूनिकोड समर्थन के साथ अपना निर्माण किया है - Wchar समर्थन को शामिल करने के लिए? mkinitcpio busyboxपैकेज में बिल्ड विकल्पों की जांच करने के लिए आप इसे छोड़ देना चाहते हैं ।
mikeserv

गिलेस - मेरा मानना ​​है कि मैंने शुरू में इस जवाब को गलत बताया - या कम से कम इसका एक हिस्सा। जबकि मुझे पूरा विश्वास है अपने mangling फ़ाइल नाम मंत्र बोलना भ्रम, निश्चित रूप से अपने होने का poor_mans_readlink बहुत अच्छी तरह से किया जाता है। यदि आप मुझे एक संपादन करने की दया करेंगे - कोई भी संपादन करेगा - और मुझे बाद में पिंग करके, मुझे इस पर अपना वोट उल्टा करना चाहिए।
mikeserv

1

बशर्ते आपने वर्तमान निर्देशिका पर अनुमतियाँ निष्पादित की हों - या उस निर्देशिका पर जहाँ से आपने अपनी शेल स्क्रिप्ट निष्पादित की है - यदि आप एक निर्देशिका के लिए एक संपूर्ण मार्ग चाहते हैं तो आपको इसकी आवश्यकता है cd

चरण 10 की cdकल्पना

यदि -Pविकल्प प्रभावी होता है, तो $PWDपर्यावरण चर उस स्ट्रिंग पर सेट किया जाएगा जो आउटपुट से होगा pwd -P। यदि नई निर्देशिका, या उस निर्देशिका के किसी भी अभिभावक पर, वर्तमान कार्य निर्देशिका को निर्धारित करने के लिए अपर्याप्त अनुमति है, तो $PWDपर्यावरण चर का मान अनिर्दिष्ट है।

और इसपर pwd -P

मानक आउटपुट के लिए लिखे गए पथनाम में कोई भी घटक नहीं होगा जो प्रकार के प्रतीकात्मक लिंक की फाइलों को संदर्भित करता है। यदि ऐसे कई पथनाम हैं जो pwdउपयोगिता मानक आउटपुट में लिख सकते हैं, एक एकल / स्लैश चरित्र के साथ एक शुरुआत और दो या स्लैश वर्णों के साथ एक या अधिक शुरुआत, तो यह एक एकल / स्लैश चरित्र के साथ आरंभनाम लिखेगा। पाथनाम में प्रमुख या दो / स्लेश वर्णों के बाद कोई अनावश्यक / स्लेश वर्ण नहीं होंगे।

ऐसा इसलिए cd -Pहै क्योंकि वर्तमान वर्किंग डायरेक्टरी को सेट pwd -Pकरना है कि अन्यथा क्या प्रिंट होना चाहिए और यह cd -है $OLDPWDकि निम्नलिखित कामों को प्रिंट करना है:

mkdir ./dir
ln -s ./dir ./ln
cd ./ln ; cd . ; cd -

आउटपुट

/home/mikeserv/test/ln

इसके लिए प्रतीक्षा कीजिए...

cd -P . ; cd . ; cd -

आउटपुट

/home/mikeserv/test/dir

और जब मैं प्रिंट कर cd -रहा हूं तो मैं प्रिंट कर रहा हूं $OLDPWDcdसेट $PWDही मुझे के रूप में cd -P . $PWDअब के लिए एक निरपेक्ष पथ है /तो मैं किसी भी अन्य चर जरूरत नहीं है -। और वास्तव में, मुझे अनुगामी की भी आवश्यकता नहीं होनी चाहिए, .लेकिन जब कोई अनवांटेड नहीं है, तो एक इंटरैक्टिव शेल में रीसेट $PWDकरने का एक निर्दिष्ट व्यवहार है। इसलिए इसे विकसित करना एक अच्छी आदत है।$HOMEcd

तो बस पथ पर उपरोक्त कार्य करने के लिए पथ ${0%/*}को सत्यापित करने के लिए पर्याप्त से अधिक होना चाहिए $0, लेकिन उस मामले में जो $0स्वयं एक नरम-लिंक है, आप संभवतः इसमें निर्देशिका नहीं बदल सकते हैं, दुर्भाग्य से।

यहाँ एक फ़ंक्शन है जो संभाल लेगा:

zpath() { cd -P . || return
    _out() { printf "%s$_zdlm\n" "$PWD/${1##*/}"; }
    _cd()  { cd -P "$1" ; } >/dev/null 2>&1
    while [ $# -gt 0 ] && _cd .
    do  if     _cd "$1" 
        then   _out
        elif ! [ -L "$1" ] && [ -e "$1" ] 
        then   _cd "${1%/*}"; _out "$1"
        elif   [ -L "$1" ]
        then   ( while set -- "${1%?/}"; _cd "${1%/*}"; [ -L "${1##*/}" ]
                 do    set " $1" "$(_cd -; ls -nd -- "$1"; echo /)"
                       set -- "${2#*"$1" -> }"
                 done; _out "$1" 
    );  else   ( PS4=ERR:\ NO_SUCH_PATH; set -x; : "$1" )
    fi; _cd -; shift; done
    unset -f _out _cd; unset -v _zdlm                                    
}

यह वर्तमान शेल में उतना ही करने का प्रयास करता है - एक सब-इनवॉइस को लागू किए बिना - हालांकि त्रुटियों और सॉफ्ट लिंक के लिए सब-इनक्लूड किए गए हैं जो निर्देशिकाओं को इंगित नहीं करते हैं। यह POSIX- संगत शेल और POSIX- संगत के lsसाथ-साथ एक साफ _function()नाम स्थान पर निर्भर करता है । यह अभी भी उत्तरार्द्ध के बिना ठीक काम करेगा, हालांकि यह unsetउस मामले में कुछ वर्तमान शेल फ़ंक्शन को अधिलेखित कर सकता है । सामान्य तौर पर इन सभी निर्भरताओं को यूनिक्स मशीन पर काफी भरोसेमंद रूप से उपलब्ध होना चाहिए।

पहली बात के साथ या बिना बहस के इसे $PWDअपने विहित मूल्य पर रीसेट कर दिया जाता है - यह आवश्यक होने पर अपने लक्ष्य के लिए किसी भी लिंक को हल करता है। तर्कों के बिना बुलाया और इसके बारे में है; लेकिन उनके साथ कहा जाता है और यह हल करेगा और प्रत्येक के लिए पथ को कैनोनिज़लाइज़ करेगा और एक संदेश को प्रिंट करेगा stderrकि क्यों नहीं।

क्योंकि यह ज्यादातर वर्तमान शेल में संचालित होता है इसलिए इसे किसी भी लम्बाई की तर्क सूची को संभालने में सक्षम होना चाहिए। यह $_zdlmचर के लिए भी दिखता है (जो कि यह unsetतब भी होता है जब यह थ्रू होता है) और इसके सी-एस्केप मूल्य को तुरंत अपने प्रत्येक तर्क के दाईं ओर प्रिंट करता है, जिनमें से प्रत्येक को हमेशा एक एकल \nपंक्ति वर्ण द्वारा भी अनुसरण किया जाता है ।

यह बहुत सारी निर्देशिका को बदल देता है, लेकिन, इसे अपने विहित मूल्य पर सेट करने के अलावा, यह प्रभावित नहीं करता है $PWD, हालांकि $OLDPWDकिसी भी माध्यम से इसे तब तक नहीं गिना जा सकता है जब यह माध्यम से हो।

यह जितनी जल्दी हो सके अपने प्रत्येक तर्क को छोड़ने की कोशिश करता है। यह करने के लिए पहले की कोशिश करता cdमें $1। यदि यह तर्क के कैनोनिकल पथ को प्रिंट करता है stdout। यदि यह $1मौजूद नहीं है तो यह जांच सकता है कि यह सॉफ्ट लिंक नहीं है। अगर सच है, तो यह प्रिंट करता है।

इस तरह यह किसी भी फ़ाइल प्रकार तर्क को संभालता है जिसे शेल को संबोधित करने की अनुमति है जब तक $1कि एक प्रतीकात्मक लिंक न हो जो किसी निर्देशिका को इंगित नहीं करता है। उस स्थिति में यह whileएक सबशेल में लूप कहलाता है ।

यह lsलिंक को पढ़ने के लिए कहता है। वर्तमान निर्देशिका को इसके प्रारंभिक मूल्य में परिवर्तित किया जाना चाहिए ताकि किसी भी संदर्भ पथ को मज़बूती से संभाल सकें और इसलिए, कमांड प्रतिस्थापन में फ़ंक्शन करता है।

cd -...ls...echo /

यह lsलिंक के नाम और स्ट्रिंग को पूरी तरह से समाहित करने के लिए इसके आउटपुट के बाईं ओर से स्ट्रिप्स होता है ->। जबकि मैंने पहली बार ऐसा करने से बचने की कोशिश की थी shiftऔर $IFSयह पता चला कि यह सबसे विश्वसनीय तरीका है, जितना मैं समझ सकता हूं। यह वही बात है जो गिल्स के खराब_मानस_रेडलिंक करता है - और यह अच्छी तरह से किया जाता है।

यह इस प्रक्रिया को एक लूप में दोहराएगा जब तक कि फ़ाइल नाम वापस lsनहीं आएगा निश्चित रूप से एक नरम लिंक नहीं है। उस समय यह उस मार्ग को पहले की तरह cdप्रिंट कर देता है।

उदाहरण का उपयोग:

zpath \
    /tmp/script \   #symlink to $HOME/test/dir/script.sh
    ln \            #symlink to ./dir/
    ln/nl \         #symlink to ../..
    /dev/fd/0 \     #currently a here-document like : dash <<\HD
    /dev/fd/1 \     #(zlink) | dash
    file \          #regular file                                             
    doesntexist \   #doesnt exist
    /dev/disk/by-path/pci-0000:00:16.2-usb-0:3:1.0-scsi-0:0:0:0 \
    /dev/./././././././null \
    . ..      

आउटपुट

/home/mikeserv/test/dir/script.sh
/home/mikeserv/test/dir/
/home/mikeserv/test/
/tmp/zshtpKRVx (deleted)
/proc/17420/fd/pipe:[1782312]
/home/mikeserv/test/file
ERR: NO_SUCH_PATH: doesntexist
/dev/sdd
/dev/null
/home/mikeserv/test/
/home/mikeserv/

या संभवतः ...

ls
dir/  file  file?  folder/  link@  ln@  script*  script3@  script4@

zdlm=\\0 zpath * | cat -A

आउटपुट

/home/mikeserv/test/dir/^@$               
/home/mikeserv/test/file^@$
/home/mikeserv/test/file$
^@$
/home/mikeserv/test/folder/^@$
/home/mikeserv/test/file$               #'link' -> 'file\n'
^@$
/home/mikeserv/test/dir/^@$             #'ln' -> './dir'
/home/mikeserv/test/script^@$
/home/mikeserv/test/dir/script.sh^@$    #'script3' -> './dir/script.sh'
/home/mikeserv/test/dir/script.sh^@$    #'script4' -> '/tmp/script' -> ...

0

जब एक एल्गोरिथ्म को पुनर्परिभाषित करने से रोकने के लिए उपलब्ध थेरेपी पायथॉन एक सहानुभूति के बारे में क्या?

function readlink { python -c "import os.path; print os.path.realpath('$1')"; }

https://stackoverflow.com/a/7305217 के समान

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.