कैसे स्क्रिप्ट के भीतर से ही एक बैश स्क्रिप्ट के स्रोत निर्देशिका प्राप्त करने के लिए


4948

मुझे उस निर्देशिका का मार्ग कैसे प्राप्त होता है जिसमें बैश स्क्रिप्ट स्थित है, उस स्क्रिप्ट के अंदर ?

मैं एक अन्य अनुप्रयोग के लिए एक लांचर के रूप में एक बैश स्क्रिप्ट का उपयोग करना चाहता हूं। मैं कार्य निर्देशिका को उसी स्थान पर बदलना चाहता हूं जहां बैश स्क्रिप्ट स्थित है, इसलिए मैं उस निर्देशिका में फ़ाइलों पर काम कर सकता हूं, जैसे:

$ ./application

69
वर्तमान समाधानों में से कोई भी काम नहीं करता है अगर निर्देशिका नाम के अंत में कोई नई रूपरेखा है - उन्हें कमांड प्रतिस्थापन द्वारा छीन लिया जाएगा। इसके चारों ओर काम करने के लिए आप कमांड प्रतिस्थापन के अंदर एक गैर-न्यूलाइन चरित्र को जोड़ सकते हैं - DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)"- और इसे बिना कमांड प्रतिस्थापन के हटा सकते हैं - DIR="${DIR%x}"
l0b0

80
@ jpmc26 दो बहुत ही सामान्य स्थितियाँ हैं: दुर्घटनाएँ और तोड़फोड़। एक स्क्रिप्ट अप्रत्याशित तरीकों से असफल नहीं होनी चाहिए क्योंकि किसी ने, कहीं, ए mkdir $'\n'
l0b0

24
जो कोई भी लोगों को अपने सिस्टम को उस तरह से तोड़फोड़ करने देता है, उसे इस तरह की समस्याओं का पता लगाने के लिए इसे छोड़ना नहीं चाहिए ... बहुत कम लोग इस तरह की गलती करने में सक्षम होते हैं। मुझे कभी भी बैश का उपयोग करने के 25 वर्षों में नहीं देखा गया, इस तरह की बात कहीं भी होती है .... यही कारण है कि हमारे पास टटल चेकिंग जैसी गड़बड़ी और प्रथाएं हैं (मैं शायद यह कहने के लिए लज्जित हो जाऊंगा कि :)
ओसिरिसगोत्रा

3
@ l0b0 इस बात पर विचार करें कि आपको उसी सुरक्षा की आवश्यकता होगी dirname, और यह कि निर्देशिका एक -(जैसे --help) से शुरू हो सकती है । DIR=$(reldir=$(dirname -- "$0"; echo x); reldir=${reldir%?x}; cd -- "$reldir" && pwd && echo x); DIR=${DIR%?x}। शायद यह ओवरकिल है?
स्कोर_उंडर

55
मैं इस विषय के बारे में Bash अकसर किये गए सवाल को पढ़ने का सुझाव देता हूं ।
रानी एल्बेग वेन

जवाबों:


6565
#!/bin/bash

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

एक उपयोगी वन-लाइनर है, जो आपको स्क्रिप्ट की पूरी निर्देशिका का नाम देगा, जहां से इसे बुलाया जा रहा है।

यह तब तक काम करेगा जब तक स्क्रिप्ट को खोजने के लिए उपयोग किए जाने वाले पथ का अंतिम घटक सिमिलिंक नहीं है (निर्देशिका लिंक ठीक हैं)। यदि आप भी स्क्रिप्ट के किसी लिंक को स्वयं हल करना चाहते हैं, तो आपको एक बहु-पंक्ति समाधान की आवश्यकता है:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"

यह पिछले एक उपनाम, के किसी भी संयोजन के साथ काम करेंगे source, bash -c, सिमलिंक, आदि

सावधान: यदि आप cdइस स्निपेट को चलाने से पहले किसी अलग निर्देशिका में जाते हैं, तो परिणाम गलत हो सकता है!

इसके अलावा, के लिए बाहर देखने के $CDPATHgotchas , और stderr उत्पादन दुष्प्रभाव उपयोगकर्ता रीडायरेक्ट उत्पादन के लिए चालाकी से ओवरराइड सीडी है तो बजाय (जैसे जब बुला के रूप में बच दृश्यों, सहित stderr करने के लिए update_terminal_cwd >&2मैक पर)। >/dev/null 2>&1अपने cdआदेश के अंत में जोड़ना दोनों संभावनाओं का ख्याल रखेगा।

यह समझने के लिए कि यह कैसे काम करता है, इस अधिक क्रिया रूप को चलाने का प्रयास करें:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET="$(readlink "$SOURCE")"
  if [[ $TARGET == /* ]]; then
    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
    SOURCE="$TARGET"
  else
    DIR="$( dirname "$SOURCE" )"
    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
    SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
echo "SOURCE is '$SOURCE'"
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

और यह कुछ इस तरह छपेगा:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'

27
आप उपयोगकर्ता के साथ काम करने वाले समाधान पर पहुंचने के लिए इस उत्तर को फ्यूज कर सकते हैं source <script>और साथ काम करता है bash <script>: और DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
दान मोल्डिंग

21
कभी-कभी cdSTDOUT को कुछ प्रिंट करता है! जैसे, अगर आपका $CDPATHहै .। इस मामले को कवर करने के लिए, उपयोग करेंDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"
user716468

182
यह स्वीकृत उत्तर ठीक नहीं है, यह सिम्बलिंक के साथ काम नहीं करता है और अत्यधिक जटिल है। dirname $(readlink -f $0)सही आदेश है। एक परीक्षण के लिए देखें gist.github.com/tvlooy/cbfbdb111a4ebad8b93e
tvlooy

165
@tvlooy IMO आपका उत्तर ठीक-ठीक नहीं है जैसा कि है, क्योंकि यह तब विफल होता है जब पथ में कोई स्थान होता है। एक न्यूलाइन वर्ण के विपरीत, यह असम्भव या असामान्य नहीं है। dirname "$(readlink -f "$0")"जटिलता नहीं जोड़ता है और परेशानी की न्यूनतम मात्रा के लिए अधिक मजबूत उपाय है।
एड्रियन गुंटर

9
@tvlooy आपकी टिप्पणी macOS (या शायद सामान्य रूप से BSD) संगत नहीं है, जबकि स्वीकृत उत्तर है। readlink -f $0देता है readlink: illegal option -- f
अलेक्जेंडर लजबर्गबर्ग

875

उपयोग करें dirname "$0":

#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"

pwdअकेले उपयोग करने से काम नहीं चलेगा यदि आप उस निर्देशिका से स्क्रिप्ट नहीं चला रहे हैं जो इसमें निहित है।

[matt@server1 ~]$ pwd
/home/matt
[matt@server1 ~]$ ./test2.sh
The script you are running has basename test2.sh, dirname .
The present working directory is /home/matt
[matt@server1 ~]$ cd /tmp
[matt@server1 tmp]$ ~/test2.sh
The script you are running has basename test2.sh, dirname /home/matt
The present working directory is /tmp

25
बैश से परे पोर्टेबिलिटी के लिए, $ 0 हमेशा पर्याप्त नहीं हो सकता है। यदि पथ पर आदेश मिला था, तो यह कार्य करने के लिए आपको "टाइप -p $ 0" स्थानापन्न करने की आवश्यकता हो सकती है।
डैरन

10
@ डारोन: type -pयदि स्क्रिप्ट निष्पादन योग्य है तो आप केवल इसका उपयोग कर सकते हैं । यह एक सूक्ष्म छेद भी खोल सकता है यदि स्क्रिप्ट का उपयोग करके निष्पादित किया जाता है bash test2.shऔर कहीं और निष्पादन योग्य नाम के साथ एक और स्क्रिप्ट है।
डी। शेवले

90
@ डारोन: लेकिन जब से इस सवाल को टैग किया गया है bashऔर हैश-बैंग लाइन में स्पष्ट रूप से उल्लेख है, /bin/bashमैं कहूंगा कि यह बैश पर निर्भर करने के लिए बहुत सुरक्षित है।
जोकिम सॉर

34
+1, लेकिन उपयोग करने में समस्या dirname $0यह है कि यदि निर्देशिका वर्तमान निर्देशिका है, तो आपको मिलेगा .। यह तब तक ठीक है जब तक आप स्क्रिप्ट में निर्देशिकाओं को बदलने नहीं जाते हैं और आपके द्वारा प्राप्त पथ का उपयोग करने की अपेक्षा करते हैं, dirname $0हालांकि यह निरपेक्ष था। निरपेक्ष पथ प्राप्त करने के लिए: pushd `dirname $0` > /dev/null, SCRIPTPATH=`pwd`, popd > /dev/null: pastie.org/1489386 (लेकिन निश्चित रूप से वहाँ उस मार्ग का विस्तार करने के लिए एक बेहतर तरीका है?)
टीजे Crowder

9
@ टीजे क्राउडर मुझे यकीन नहीं है कि dirname $0एक समस्या है अगर आप इसे एक चर पर असाइन करते हैं और फिर इसका उपयोग स्क्रिप्ट की तरह लॉन्च करने के लिए करते हैं $dir/script.sh; मुझे लगता है कि इस समय के 90% इस प्रकार के उपयोग का मामला है। ./script.shठीक काम करेगा।
मैट बी

513

dirnameआदेश सबसे बुनियादी, बस के बंद फ़ाइल नाम करने के लिए पथ को पार्स है $0(स्क्रिप्ट नाम) अलग-अलग:

dirname "$0"

लेकिन, जैसा कि मैट बी ने बताया है कि स्क्रिप्ट को कैसे कहा जाता है, इसके आधार पर लौटाया गया रास्ता अलग है। pwdकाम नहीं करता है क्योंकि यह केवल आपको बताता है कि वर्तमान निर्देशिका क्या है, यह नहीं है कि स्क्रिप्ट किस निर्देशिका में रहती है। इसके अलावा, यदि किसी स्क्रिप्ट के प्रतीकात्मक लिंक को निष्पादित किया जाता है, तो आपको एक (शायद रिश्तेदार) पथ मिलने वाला है। जहां लिंक रहता है, वास्तविक स्क्रिप्ट नहीं।

कुछ अन्य लोगों ने readlinkकमांड का उल्लेख किया है , लेकिन इसके सबसे सरल पर, आप इसका उपयोग कर सकते हैं:

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

readlinkफाइलसिस्टम की जड़ से स्क्रिप्ट पथ को एक पूर्ण पथ पर हल करेगा। तो, एकल या डबल डॉट्स, टिल्ड और / या प्रतीकात्मक लिंक वाले किसी भी पथ को पूर्ण पथ पर हल किया जाएगा।

इनमें से प्रत्येक को प्रदर्शित करने वाली एक स्क्रिप्ट है whatdir.sh:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"

इस स्क्रिप्ट को मेरे घर में चलाना, रिश्तेदार पथ का उपयोग करना:

>>>$ ./whatdir.sh 
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

फिर, लेकिन स्क्रिप्ट के लिए पूर्ण पथ का उपयोग कर:

>>>$ /Users/phatblat/whatdir.sh 
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

अब बदलती निर्देशिका:

>>>$ cd /tmp
>>>$ ~/whatdir.sh 
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

और अंत में स्क्रिप्ट को निष्पादित करने के लिए एक प्रतीकात्मक लिंक का उपयोग करना:

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh 
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat

13
readlinkडिफ़ॉल्ट स्थापना में कुछ प्लेटफ़ॉर्म में उपलब्ध नहीं होगा। यदि आप कर सकते हैं तो इसका उपयोग करने से बचने की कोशिश करें
TL

43
export SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
व्हाट्सएप

13
OSX Yosemite में 10.10.1 -fको एक विकल्प के रूप में मान्यता नहीं दी गई है readlink। का उपयोग stat -fकरने के बजाय काम करता है। धन्यवाद
cucu8

11
OSX में, greadlinkमूल रूप से readlinkहम सभी परिचित हैं। यहाँ एक प्लेटफ़ॉर्म स्वतंत्र संस्करण है:dir=`greadlink -f ${BASH_SOURCE[0]} || readlink -f ${BASH_SOURCE[0]}`
robert

6
अच्छी कॉल, @robert। FYI करें, greadlinkआसानी से homebrew के माध्यम से स्थापित किया जा सकता है:brew install coreutils
phatblat

184
pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}"
if ([ -h "${SCRIPT_PATH}" ]); then
  while([ -h "${SCRIPT_PATH}" ]); do cd `dirname "$SCRIPT_PATH"`; 
  SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

सहित सभी संस्करणों के लिए काम करता है

  • जब इसे मल्टीपल डेप्थ सॉफ्ट लिंक के माध्यम से बुलाया जाता है,
  • जब यह फ़ाइल
  • जब स्क्रिप्ट को कमांड " source" उर्फ .(डॉट) ऑपरेटर द्वारा बुलाया जाता है ।
  • जब $0कॉल करने वाले से arg को संशोधित किया जाता है।
  • "./script"
  • "/full/path/to/script"
  • "/some/path/../../another/path/script"
  • "./some/folder/script"

वैकल्पिक रूप से, यदि बैश स्क्रिप्ट अपने आप में एक रिश्तेदार सिम्लिंक है, जिसका आप अनुसरण करना चाहते हैं और लिंक-की गई स्क्रिप्ट का पूरा पथ वापस करना चाहते हैं :

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
if ([ -h "${SCRIPT_PATH}" ]) then
  while([ -h "${SCRIPT_PATH}" ]) do cd `dirname "$SCRIPT_PATH"`; SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

SCRIPT_PATHपूर्ण पथ में दिया गया है, चाहे इसे कैसे भी कहा जाए।
बस सुनिश्चित करें कि आप स्क्रिप्ट की शुरुआत में इसका पता लगाते हैं।

यह टिप्पणी और कोड Copyleft, GPL2.0 के तहत चयन योग्य लाइसेंस या बाद में या CC-SA 3.0 (CreativeCommons Share Alike) या बाद में। (c) 2008. सभी अधिकार सुरक्षित। किसी प्रकार की कोई वारंटी नहीं। आपको चेतावनी दी गई है।
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aa8e1e9e36cacf421b9463dd2bbaa02906d0d6656 पर प्राप्त करें।


4
अच्छा! SCRIPT_PATH = द्वारा "pushd [...] popd / dev / null" की जगह को छोटा बनाया जा सकता है readlink -f $(dirname "${VIRTUAL_ENV}");
ई-सिटिस

5
और पुशड का उपयोग करने के बजाय ...; क्या $ (cd dirname "${SCRIPT_PATH}"&& pwd) का उपयोग करना बेहतर नहीं होगा ? लेकिन वैसे भी महान स्क्रिप्ट!
ओवेन्स

6
स्क्रिप्ट के लिए यह खतरनाक cdहै कि वह वर्तमान निर्देशिका से बाहर cdआईएनजी के बाद फिर से वापस आ जाएगी: स्क्रिप्ट के पास निर्देशिका को वापस उस निर्देशिका में बदलने की अनुमति नहीं हो सकती है जो उस समय चालू थी जब इसे लागू किया गया था। (वही पुशड / पॉपड के लिए जाता है)
एड्रियन प्रोक

7
readlink -fग्नू-विशिष्ट है। बीएसडी के readlinkपास वह विकल्प नहीं है।
कारा ब्राइटवेल

3
सभी अनावश्यक उपधाराओं के साथ क्या है? ([ ... ])की तुलना में कम कुशल है [ ... ], और उस प्रदर्शन के बदले में दिए गए अलगाव का कोई लाभ नहीं लिया गया है।
चार्ल्स डफी

110

संक्षिप्त जवाब:

`dirname $0`

या ( अधिमानतः ):

$(dirname "$0")

17
यदि आप स्क्रिप्ट का स्रोत बनाते हैं तो यह काम नहीं करेगा। "source my / script.sh"
अरुणप्रसाद राजकुमार

मैं अपनी बैश लिपियों में हर समय इस का उपयोग करता हूं जो सामान को स्वचालित करता है और अक्सर एक ही डायर में अन्य लिपियों का आह्वान करता है। मैं कभी भी sourceइन पर प्रयोग नहीं करूंगा और cd $(dirname $0)याद रखना आसान है।
kqw

16
@vidstige: ${BASH_SOURCE[0]}बजाय $0काम करेगाsource my/script.sh
टिमोथी जोन्स

@TimothyJones जो उस समय के 100% को विफल कर देगा यदि बैश के अलावा किसी अन्य शेल से खट्टा हो। ${BASH_SOURCE[0]}संतोषजनक नहीं है। ${BASH_SOURCE:-0}ज़्यादा बेहतर है।
मैथ्यू CAROFF

106

आप उपयोग कर सकते हैं $BASH_SOURCE:

#!/bin/bash

scriptdir=`dirname "$BASH_SOURCE"`

ध्यान दें कि आपको बैश एक्सटेंशन के बाद से इसका उपयोग करने की आवश्यकता है #!/bin/bashऔर नहीं #!/bin/sh


14
जब मैं करता हूं ./foo/script, तब $(dirname $BASH_SOURCE)है ./foo
तक

1
@ टिल, इस मामले में हम realpath./foo/script का पूर्ण पथ प्राप्त करने के लिए कमांड का उपयोग कर सकते हैं । तो dirname $(realpath ./foo/script) स्क्रिप्ट का रास्ता देगा।
पुरुषोत्तमन पोवई

73

यह करना चाहिए:

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

यह पथ में सिमिलिंक और रिक्त स्थान के साथ काम करता है।

dirnameऔर के लिए पृष्ठों को देखेंreadlink

टिप्पणी ट्रैक से यह मैक ओएस के साथ काम नहीं करता है। मुझे नहीं पता कि ऐसा क्यों है। कोई सुझाव?


6
अपने समाधान के साथ, पूरी निर्देशिका पथ के बजाय ./script.shशो जैसे स्क्रिप्ट को लागू करना.
ब्रूनो नेग्रो ज़ीका

5
MacOS पर रीडलिंक के लिए कोई विकल्प नहीं है। statइसके बजाय उपयोग करें । लेकिन फिर भी, यह दिखाता है .कि क्या आप 'इस' डायर में हैं।
डेनिस द मेनेसे

2
आपको coreutilsहोमब्रे से इंस्टॉल करने और मैकओएस पर विकल्प greadlinkप्राप्त करने के लिए उपयोग करने की आवश्यकता है -fक्योंकि यह कवर के तहत * बीएसडी है और लिनक्स नहीं है।
ड्रैगन 788

आपको सभी दाहिने हाथ की ओर डबल कोट्स को जोड़ना चाहिए:DIR="$(dirname "$(readlink -f "$0")")"
हैगेलो

60

pwdवर्तमान कार्यशील निर्देशिका dirnameको खोजने के लिए , और किसी विशेष फ़ाइल की निर्देशिका को खोजने के लिए इस्तेमाल किया जा सकता है (कमांड जो चलाया गया था, है $0, इसलिएdirname $0 आपको वर्तमान स्क्रिप्ट की डायरेक्टरी देनी चाहिए)।

हालांकि, dirnameफ़ाइल नाम के डायरेक्टरी भाग को ठीक-ठीक देता है, जो वर्तमान वर्किंग डाइरेक्टरी के सापेक्ष नहीं होने की संभावना से अधिक है। यदि आपकी स्क्रिप्ट को किसी कारण से निर्देशिका बदलने की आवश्यकता है, तो इससे आउटपुटdirname अर्थहीन हो जाता है।

मैं निम्नलिखित सुझाव देता हूं:

#!/bin/bash

reldir=`dirname $0`
cd $reldir
directory=`pwd`

echo "Directory is $directory"

इस तरह, आपको एक पूर्ण, बल्कि सापेक्ष निर्देशिका मिलती है।

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

हालाँकि बस

cd `dirname $0`

प्रश्न में विशिष्ट परिदृश्य को हल करता है, मुझे लगता है कि आम तौर पर और अधिक उपयोगी के लिए पूर्ण पथ है।


9
आप इसे इस तरह से एक पंक्ति में कर सकते हैं: DIRECTORY = $ (cd dirname $0&& pwd)
dogbane

यह काम नहीं करता है यदि स्क्रिप्ट किसी अन्य स्क्रिप्ट को स्रोत बनाती है और आप बाद का नाम जानना चाहते हैं।
रीइनियरियरपोस्ट

52

मैं स्वीकार किए गए उत्तर में वन-लाइनर को कॉपी करने के लिए इस पृष्ठ पर आने के लिए थक गया हूं। इसके साथ समस्या यह है कि इसे समझना और याद रखना आसान नहीं है।

यहाँ एक आसानी से याद की जाने वाली स्क्रिप्ट है:

DIR="$(dirname "${BASH_SOURCE[0]}")"  # get the directory name
DIR="$(realpath "${DIR}")"    # resolve its full path if need be

2
या, अधिक अस्पष्ट रूप से, एक पंक्ति पर: DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
agc

यह स्वीकृत उत्तर क्यों नहीं है? क्या realpathलूप के साथ "मैन्युअल रूप से" हल करने से कोई अंतर है readlink? यहां तक ​​कि readlinkमैन पेज भी कहता हैNote realpath(1) is the preferred command to use for canonicalization functionality.
User9123

1
और जिस तरह से हमें realpathपहले आवेदन नहीं करना चाहिए था dirname, उसके बाद नहीं? यदि स्क्रिप्ट फ़ाइल अपने आप में एक सिमलिंक है ... तो यह कुछ ऐसा देगी DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"। वास्तव में साइमन द्वारा प्रस्तावित उत्तर के बहुत करीब।
User9123

@ User9123 मुझे लगता है कि स्वीकार करने की कोशिश सभी लोकप्रिय शेल / डिस्ट्रो के साथ संगत होने की कोशिश है। अधिक से अधिक, आप जो करने की कोशिश कर रहे हैं, उसके आधार पर, ज्यादातर मामलों में लोग उस निर्देशिका को प्राप्त करना चाहते हैं जहां वास्तविक स्रोत की निर्देशिका के बजाय सिमलिंक स्थित है।
वांग

37

मुझे नहीं लगता कि यह उतना आसान है जितना दूसरों ने इसे बनाया है। pwdकाम नहीं करता है, क्योंकि वर्तमान निर्देशिका जरूरी स्क्रिप्ट के साथ निर्देशिका नहीं है। $0हमेशा या तो जानकारी नहीं है। एक स्क्रिप्ट आह्वान करने के लिए निम्नलिखित तीन तरीकों पर विचार करें:

./script

/usr/bin/script

script

पहले और तीसरे तरीके $0में पूर्ण पथ की जानकारी नहीं है। दूसरे और तीसरे में, pwdकाम नहीं करता है। तीसरे तरीके से निर्देशिका प्राप्त करने का एकमात्र तरीका पथ के माध्यम से चलना होगा और सही मिलान के साथ फ़ाइल ढूंढना होगा। मूल रूप से कोड को फिर से करना होगा जो ओएस करता है।

एक तरीका यह है कि आप जो पूछ रहे हैं वह /usr/shareनिर्देशिका में डेटा को केवल हार्डकोड करना होगा , और इसे अपने पूर्ण पथ द्वारा संदर्भित करना होगा। डेटा /usr/binवैसे भी डायरेक्टरी में नहीं होता है, इसलिए शायद यही करना है।


9
यदि आप उसकी टिप्पणी को अनसुना करना चाहते हैं, तो साबित करें कि एक स्क्रिप्ट एक कोड उदाहरण के साथ संग्रहीत की जा सकती है।
रिचर्ड ड्यूयर

34
SCRIPT_DIR=$( cd ${0%/*} && pwd -P )

यह चुने हुए उत्तर की तुलना में छोटा है। और बस काम करने के लिए प्रकट होता है। यह 1000 वोटों का हकदार है, इसलिए लोग इसे नजरअंदाज नहीं करते हैं।
पैट्रिक

2
पिछले उत्तरों में से कई के बारे में विस्तार से बताया गया है, न तो सही जानकारी होने की गारंटी दी जाती है $0और न ही pwdइस बात पर निर्भर करता है कि स्क्रिप्ट कैसे लागू की जाती है।
IMSoP

34
$(dirname "$(readlink -f "$BASH_SOURCE")")

मैं पसंद करता हूँ, $BASH_SOURCEअधिक $0, क्योंकि यह पाठकों के लिए भी स्पष्ट है, जो अच्छी तरह से वाकिफ नहीं है। $(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
ब्लोबमास्टर

32

यह मैक ओएस एक्स 10.6.6 पर वर्तमान कार्यशील निर्देशिका प्राप्त करता है:

DIR=$(cd "$(dirname "$0")"; pwd)

27

यह लिनक्स विशिष्ट है, लेकिन आप इसका उपयोग कर सकते हैं:

SELF=$(readlink /proc/$$/fd/255)

1
यह बैश विशिष्ट भी है, लेकिन शायद बैश का व्यवहार बदल गया है? /proc/fd/$$/255एक निर्देशिका के लिए, tty को इंगित करने के लिए लगता है। उदाहरण के लिए, मेरे वर्तमान लॉगिन शेल में, फाइल डिस्क्रिप्टर 0, 1, 2, और 255 सभी देखें /dev/pts/4। किसी भी मामले में, पार्टी के मैनुअल 255 fd उल्लेख नहीं है, तो यह इस व्यवहार पर निर्भर रहना शायद मूर्ख है \।
कीथ थॉम्पसन

2
इंटरेक्टिव शेल! = स्क्रिप्ट। वैसे भी realpath ${BASH_SOURCE[0]};जाने के लिए सबसे अच्छा तरीका होगा।
स्टीव बेकर

23

यहाँ एक POSIX आज्ञाकारी एक लाइनर है:

SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`

# test
echo $SCRIPT_PATH

4
मुझे इसकी सफलता तब मिली जब किसी स्क्रिप्ट को स्वयं या सुडो का उपयोग करके, लेकिन स्रोत को कॉल करते समय नहीं ।/script.sh
माइकल आर

और यह विफल हो जाता है जब cdनए पथ नाम को प्रिंट करने के लिए कॉन्फ़िगर किया जाता है।
आरोन दिगुल्ला

18

मैंने इन सभी की कोशिश की और कोई भी काम नहीं किया। एक बहुत करीब था, लेकिन एक छोटा बग था जिसने इसे बुरी तरह से तोड़ दिया; वे उद्धरण चिह्नों में पथ लपेटना भूल गए।

बहुत सारे लोग यह मान लेते हैं कि आप स्क्रिप्ट को शेल से चला रहे हैं, इसलिए जब आप कोई नई स्क्रिप्ट खोलते हैं तो यह भूल जाते हैं कि यह आपके घर में मौजूद है।

इस निर्देशिका को आकार के लिए आज़माएँ:

/var/No one/Thought/About Spaces Being/In a Directory/Name/And Here's your file.text

यह सही है कि आप इसे कैसे या कहाँ चलाते हैं:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"

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

cd "`dirname "$0"`"

4
यदि स्क्रिप्ट किसी अन्य स्क्रिप्ट से सॉरी की जा रही है तो काम नहीं करता है।
रीइनियरियरपोस्ट

यह काम नहीं करता है यदि $ 0 का अंतिम भाग किसी अन्य निर्देशिका की प्रविष्टि की ओर इशारा करते हुए एक प्रतीकात्मक लिंक है ( ln -s ../bin64/foo /usr/bin/foo)।
हैगेलो

17

यहाँ सरल, सही तरीका है:

actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")

स्पष्टीकरण:

  • ${BASH_SOURCE[0]}- स्क्रिप्ट का पूरा रास्ता। इसकी वैल्यू तब भी सही होगी जब स्क्रिप्ट को सॉरी किया जा रहा हो, जैसे कि source <(echo 'echo $0')प्रिंट्स को बैश कर दें , जबकि इसकी जगह ${BASH_SOURCE[0]}स्क्रिप्ट का पूरा रास्ता प्रिंट कर लें। (बेशक, यह मानता है कि आप बैश पर निर्भरता ले रहे हैं।)

  • readlink -f- निर्दिष्ट पथ में पुन: किसी भी सिमिलिंक को हल करता है। यह एक GNU एक्सटेंशन है, और BSD सिस्टम पर (उदाहरण के लिए) उपलब्ध नहीं है। यदि आप एक मैक चला रहे हैं, तो आप GNU को स्थापित करने coreutilsऔर इसके साथ इसे दबाने के लिए Homebrew का उपयोग कर सकते हैं greadlink -f

  • और निश्चित रूप dirnameसे मार्ग की मूल निर्देशिका मिलती है।


1
greadlink -fदुर्भाग्य से प्रभावी ढंग से काम नहीं करता है जब sourceमैक पर स्क्रिप्ट आईएनजी :(
गैब कोपले

17

ऐसा करने का सबसे छोटा और सबसे सुंदर तरीका है:

#!/bin/bash
DIRECTORY=$(cd `dirname $0` && pwd)
echo $DIRECTORY

यह सभी प्लेटफार्मों पर काम करेगा और सुपर क्लीन है।

अधिक विवरण में पाया जा सकता है कि " कौन सी निर्देशिका उस बैश स्क्रिप्ट में है? "।


महान स्वच्छ समाधान, लेकिन यह काम नहीं करेगा यदि फ़ाइल को सीलिंक किया गया है।
रूबल

16

मैं कुछ इस तरह का उपयोग करेंगे:

# retrieve the full pathname of the called script
scriptPath=$(which $0)

# check whether the path is a link or not
if [ -L $scriptPath ]; then

    # it is a link then retrieve the target path and get the directory name
    sourceDir=$(dirname $(readlink -f $scriptPath))

else

    # otherwise just get the directory name of the script path
    sourceDir=$(dirname $scriptPath)

fi

यह असली है! सरल के साथ shभी काम करता है! सरल dirname "$0"आधारित समाधानों के साथ समस्या : यदि स्क्रिप्ट अंदर है $PATHऔर बिना पथ के लागू होती है, तो वे गलत परिणाम देंगे।
नॉटिलेटर

@Notinlist ऐसा नहीं है। यदि स्क्रिप्ट के माध्यम से पाया जाता है PATH, $0तो पूर्ण फ़ाइल नाम शामिल होगा। यदि स्क्रिप्ट किसी रिश्तेदार या पूर्ण फ़ाइल नाम से युक्त है /, $0तो उसमें वह शामिल होगा।
नील मैय्यू

खट्टी स्क्रिप्ट के लिए काम नहीं करेगा।
अमित नायडू

16

यह समाधान ई-सिटिस के लिए एक मामूली संशोधन है और 3bcdnlklvc04a उनके उत्तर में बताया गया है :

SCRIPT_DIR=''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
    SCRIPT_DIR="$PWD"
    popd > /dev/null
}    

यह अभी भी सूचीबद्ध सभी मामलों में काम करना चाहिए।

यह popdएक विफल होने के बाद रोकेगा pushd, konsolebox का धन्यवाद।


यह पूरी तरह से "असली" dirname प्राप्त करने के लिए काम करता है, बजाय केवल एक सिमलिंक के नाम से। धन्यवाद!
जय टेलर

1
बेहतरSCRIPT_DIR=''; pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && { SCRIPT_DIR=$PWD; popd > /dev/null; }
konsolebox

@konsolebox, आप किससे बचाव करने की कोशिश कर रहे हैं? मैं आम तौर पर लॉजिकल कंडीशंस के लिए एक प्रशंसक हूं, लेकिन उस विशिष्ट त्रुटि का क्या जो आप पुशड में देख रहे थे? मैं एक खाली SCRIPT_DIR को वापस करने के बजाय सीधे इसे संभालने का एक तरीका खोजूंगा।
फुवजैक्स

@Fwjjax popdमामलों में (भले ही दुर्लभ) pushdविफल होने से बचने के लिए प्राकृतिक अभ्यास । और मामले में pushdविफल होने पर आपको क्या लगता है कि इसका मूल्य क्या होना चाहिए SCRIPT_DIR? तार्किक या जो एक उपयोगकर्ता पसंद कर सकता है, लेकिन निश्चित रूप popdसे गलत है , के आधार पर कार्रवाई भिन्न हो सकती है।
konsolebox

उन सभी pushd popdखतरों को केवल उन्हें छोड़ने और बजाय आदेश प्रतिस्थापन में संलग्न cd+ का उपयोग करके टाला जा सकता है pwdSCRIPT_DIR=$(...)
अमित नायडू

16

GNU कोरुटिल्स readlink(उदाहरण के लिए लिनक्स) वाले सिस्टम के लिए :

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

स्क्रिप्ट फ़ाइल नाम में BASH_SOURCEहोने पर उपयोग करने की कोई आवश्यकता नहीं है $0


2
जब तक स्क्रिप्ट के साथ खटास नहीं आई। या 'स्रोत' जिस स्थिति में यह अभी भी होगा जो भी स्क्रिप्ट ने इसे सॉर्ट किया है, या, यदि कमांड लाइन से, '-बैश' (टैटी लॉगिन) या 'बैश' ('बैश-एल' के माध्यम से) या '/ बिन / बैश '(एक संवादात्मक गैर-लॉगिन शेल के रूप में आमंत्रित)
ओसिरिसगोथरा

मैंने dirnameकॉल के आसपास उद्धरणों की दूसरी जोड़ी जोड़ी । यदि निर्देशिका पथ में स्थान हो, तो आवश्यक है।
user1338062

14
#!/bin/sh
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`
done

scriptdir=`dirname "$PRG"`

मैंने विभिन्न प्रणालियों में इसका परीक्षण नहीं किया है। लेकिन यह समाधान वह है जो कम से कम उबंटू पर काम करता है, मेरे लिए!
नेटस ड्रू

$0एक पटकथा वाली स्क्रिप्ट के लिए काम नहीं करेगा
अमित नायडू

13

$_के विकल्प के रूप में ध्यान देने योग्य है $0। यदि आप बैश से स्क्रिप्ट चला रहे हैं, तो स्वीकृत उत्तर को छोटा किया जा सकता है:

DIR="$( dirname "$_" )"

ध्यान दें कि यह आपकी स्क्रिप्ट में पहला स्टेटमेंट होना चाहिए।


4
यह टूट जाता है अगर आप sourceया .स्क्रिप्ट। उन स्थितियों में, $_आपके द्वारा चलाए गए अंतिम कमांड का अंतिम पैरामीटर होगा .$BASH_SOURCEहर बार काम करता है।
क्लैके

11

मैंने दिए गए उत्तरों में से कई की तुलना की है, और कुछ और कॉम्पैक्ट समाधानों के साथ आया हूं। ये पागल किनारे के सभी मामलों को संभालते हैं जो आपके पसंदीदा संयोजन से उत्पन्न होते हैं:

  • निरपेक्ष पथ या सापेक्ष पथ
  • फ़ाइल और निर्देशिका नरम लिंक
  • मंगलाचरण के रूप में script, bash script, bash -c script, source script, या. script
  • निर्देशिका और / या फ़ाइल नाम में रिक्त स्थान, टैब, newlines, यूनिकोड, आदि
  • एक हाइफ़न के साथ शुरू होने वाले फ़ाइलनाम

यदि आप लिनक्स से भाग रहे हैं, तो ऐसा लगता है कि procवर्तमान में चल रही स्क्रिप्ट के पूर्ण रूप से हल किए गए स्रोत का पता लगाने के लिए हैंडल का उपयोग करना सबसे अच्छा समाधान है (एक संवादात्मक सत्र में, लिंक संबंधित को इंगित करता है /dev/pts/X):

resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"

इसके पास एक छोटा सा कुरूपता है, लेकिन फिक्स कॉम्पैक्ट और समझने में आसान है। हम केवल bash primitives का उपयोग नहीं कर रहे हैं, लेकिन मैं इसके साथ ठीक हूं क्योंकि readlinkयह कार्य को सरल बनाता है। echo Xएक कहते हैं Xचर स्ट्रिंग तो के अंत तक कि फ़ाइल नाम में किसी भी पिछली श्वेत रिक्ति को खाया नहीं होता है, और पैरामीटर प्रतिस्थापन ${VAR%X}पंक्ति के अंत में से छुटकारा मिलता है X। क्योंकि readlinkअपनी खुद की एक नई पंक्ति जोड़ता है (जो सामान्य रूप से कमांड प्रतिस्थापन में खाया जाएगा यदि हमारी पिछली चाल के लिए नहीं), तो हमें उससे भी छुटकारा पाना होगा। यह $''उद्धरण योजना का उपयोग करके सबसे आसानी से पूरा किया जाता है , जो हमें भागने के अनुक्रम जैसे कि उपयोग करने देता है\n नई सूचियों का प्रतिनिधित्व करने के लिए (यह भी है कि आप आसानी से नामांकित निर्देशिकाओं और फ़ाइलों को आसानी से कैसे बना सकते हैं)।

उपरोक्त को लिनक्स पर वर्तमान में चल रही स्क्रिप्ट का पता लगाने के लिए अपनी आवश्यकताओं को कवर करना चाहिए, लेकिन यदि आपके पास procअपने निपटान में फाइल सिस्टम नहीं है , या यदि आप किसी अन्य फ़ाइल के पूरी तरह से हल किए गए रास्ते का पता लगाने की कोशिश कर रहे हैं, तो शायद आप कर सकते हैं नीचे दिए गए कोड को उपयोगी पाते हैं। यह उपरोक्त वन-लाइनर से केवल एक मामूली संशोधन है। यदि आप अजीब निर्देशिका / फ़ाइलनामों के साथ खेल रहे हैं, तो दोनों के साथ आउटपुट की जाँच करें lsऔर readlinkजानकारीपूर्ण है, जैसा lsकि "सरलीकृत" रास्तों को आउटपुट करेगा, ?नई कहानियों जैसी चीजों के लिए प्रतिस्थापन ।

absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}

ls -l -- "$dir/$file"
printf '$absolute_path: "%s"\n' "$absolute_path"

मुझे /dev/pts/30Ubuntu 14.10 डेस्कटॉप पर बैश मिलता है ।
डैन डस्केल्सस्कु

@DanDascalescu वन-लाइनर का उपयोग कर रहा है? या तल पर पूर्ण कोड स्निपेट? और क्या आप इसे किसी भी मुश्किल रास्ते से खिला रहे थे?
billyjmc

एक लाइन के साथ साथ के लिए एक और लाइन echo $resolved, मैं के रूप में यह बचा लिया d, chmod +x d, ./d
दान डस्केल्सस्कु

@DanDascalescu आपकी स्क्रिप्ट में पहली पंक्ति होने की ज़रूरत है#!/bin/bash
billyjmc

10

प्रयोग करके देखें:

real=$(realpath $(dirname $0))

1
सब मैं जानना चाहता हूं कि यह तरीका अच्छा क्यों नहीं है? यह मेरे लिए कोई बुरा और सही नहीं था। किसी को भी समझा सकता है कि यह नीचा क्यों है?
शाऊ हां

7
realpath मानक उपयोगिता नहीं है।
स्टीव बेनेट

2
लिनक्स पर, realpath एक मानक उपयोगिता (GNU कोरुटिल्स पैकेज का हिस्सा) है, लेकिन यह एक बैश बिल्ट-इन (यानी, bash द्वारा प्रदान किया गया एक फ़ंक्शन) नहीं है। आप Linux चला रहे हैं, इस विधि शायद काम करते हैं, हालांकि मैं स्थानापन्न होता होगा $0के लिए ${BASH_SOURCE[0]}इतना है कि इस विधि कहीं भी काम करेंगे, एक समारोह में भी शामिल है।
डग रिचर्डसन

3
इस उत्तर में संचालन का क्रम गलत है। आपको पहले सिमलिंक को हल करने की आवश्यकता है , फिर ऐसा करें dirnameक्योंकि $0हो सकता है कि आखिरी हिस्सा सिम्लिंक हो जो एक ऐसी फ़ाइल की ओर इशारा करता है जो सिर्फ़ सिलिंक के समान निर्देशिका में नहीं है। इस उत्तर में वर्णित समाधान को केवल उस निर्देशिका का मार्ग प्राप्त होता है जहां सहारे को संग्रहीत किया जाता है, लक्ष्य की निर्देशिका को नहीं। इसके अलावा, यह समाधान गायब है। यदि पथ में विशेष वर्ण हैं तो यह काम नहीं करेगा।
हैगेलो

3
dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
कोस्टिएंटिन पोनोमारेंको

9

निम्नलिखित क्रॉस-संगत समाधान आज़माएं:

CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"

जैसे आदेश ( realpathया readlinkऑपरेटिंग सिस्टम के आधार पर) उपलब्ध नहीं हो सकते हैं।

नोट: बैश में, इसके ${BASH_SOURCE[0]}बजाय का उपयोग करने की अनुशंसा की जाती है $0, अन्यथा फ़ाइल ( source/ .) सोर्सिंग करते समय पथ टूट सकता है ।

वैकल्पिक रूप से आप निम्नलिखित फ़ंक्शन को bash में आज़मा सकते हैं:

realpath () {
  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

यह फ़ंक्शन 1 तर्क लेता है। यदि तर्क में पहले से ही पूर्ण पथ है, तो इसे प्रिंट करें जैसा कि यह है, अन्यथा $PWDचर + फ़ाइल नाम तर्क ( ./उपसर्ग के बिना ) प्रिंट करें ।

सम्बंधित:


कृपया realpath फ़ंक्शन के बारे में अधिक बताएं।
क्रिस

1
@ क्रिस realpathसमारोह 1 तर्क लेता है। यदि तर्क में पहले से ही पूर्ण पथ है, तो इसे प्रिंट करें जैसा कि है, अन्यथा प्रिंट करें $PWD+ फ़ाइल नाम (बिना ./उपसर्ग के)।
kenorb

स्क्रिप्ट के सममित होने पर आपका क्रॉस-संगत समाधान काम नहीं करता है।
जकूब जिरुटका

9

मुझे विश्वास है कि मुझे यह मिल गया है। मुझे पार्टी में देर हो गई है, लेकिन मुझे लगता है कि कुछ इस बात की सराहना करेंगे कि अगर वे इस सूत्र में आते हैं। टिप्पणियों की व्याख्या करनी चाहिए:

#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.

## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
## dereference symbolic links (ala 'readlink') until the originating file
## is found. This is effectively the same function provided in stdlib.h as
## 'realpath' and on the command line in GNU 'readlink -f'.

## Neither of these tools, however, are particularly accessible on the many
## systems that do not have the GNU implementation of readlink, nor ship
## with a system compiler (not to mention the requisite knowledge of C).

## This script is written with portability and (to the extent possible, speed)
## in mind, hence the use of printf for echo and case statements where they
## can be substituded for test, though I've had to scale back a bit on that.

## It is (to the best of my knowledge) written in standard POSIX shell, and
## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
## issues with it, though I'm not sure why; so probably best to avoid for now.

## Particularly useful (in fact, the reason I wrote this) is the fact that
## it can be used within a shell script to find the path of the script itself.
## (I am sure the shell knows this already; but most likely for the sake of
## security it is not made readily available. The implementation of "$0"
## specificies that the $0 must be the location of **last** symbolic link in
## a chain, or wherever it resides in the path.) This can be used for some
## ...interesting things, like self-duplicating and self-modifiying scripts.

## Currently supported are three errors: whether the file specified exists
## (ala ENOENT), whether its target exists/is accessible; and the special
## case of when a sybolic link references itself "foo -> foo": a common error
## for beginners, since 'ln' does not produce an error if the order of link
## and target are reversed on the command line. (See POSIX signal ELOOP.)

## It would probably be rather simple to write to use this as a basis for
## a pure shell implementation of the 'symlinks' util included with Linux.

## As an aside, the amount of code below **completely** belies the amount
## effort it took to get this right -- but I guess that's coding for you.

##===-------------------------------------------------------------------===##

for argv; do :; done # Last parameter on command line, for options parsing.

## Error messages. Use functions so that we can sub in when the error occurs.

recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
errnoent(){ printf "No such file: "$@"\n" ;} # Borrow a horrible signal name.

# Probably best not to install as 'pathfull', if you can avoid it.

pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"

## 'test and 'ls' report different status for bad symlinks, so we use this.

 if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null;  then
    errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ];   then
    recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -z "$link" ]; then
    dangling 1>&2; exit 1; fi
 fi

## Not a link, but there might be one in the path, so 'cd' and 'pwd'.

 if [ -z "$link" ]; then if [ "$(dirname "$@" | cut -c1)" = '/' ]; then
   printf "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\n"; fi; exit 0
 fi

## Walk the symlinks back to the origin. Calls itself recursivly as needed.

 while [ "$link" ]; do
   cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
   case "$newlink" in
    "$link") dangling 1>&2 && exit 1                                       ;;
         '') printf "$(pwd)/$(basename "$link")\n"; exit 0                 ;;
          *) link="$newlink" && pathfull "$link"                           ;;
   esac
 done
 printf "$(pwd)/$(basename "$newlink")\n"
}

## Demo. Install somewhere deep in the filesystem, then symlink somewhere 
## else, symlink again (maybe with a different name) elsewhere, and link
## back into the directory you started in (or something.) The absolute path
## of the script will always be reported in the usage, along with "$0".

if [ -z "$argv" ]; then scriptname="$(pathfull "$0")"

# Yay ANSI l33t codes! Fancy.
 printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
 printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         \033[4m$scriptname\033[24m\n\n        "
 printf " From within an invocation of a script, locate the script's "
 printf "own file\n         (no matter where it has been linked or "
 printf "from where it is being called).\n\n"

else pathfull "$@"
fi

8

ये स्क्रिप्ट जानकारी प्राप्त करने के छोटे तरीके हैं:

फ़ोल्डर और फाइलें:

    Script: "/tmp/src dir/test.sh"
    Calling folder: "/tmp/src dir/other"

इन आदेशों का उपयोग करना:

    echo Script-Dir : `dirname "$(realpath $0)"`
    echo Script-Dir : $( cd ${0%/*} && pwd -P )
    echo Script-Dir : $(dirname "$(readlink -f "$0")")
    echo
    echo Script-Name : `basename "$(realpath $0)"`
    echo Script-Name : `basename $0`
    echo
    echo Script-Dir-Relative : `dirname "$BASH_SOURCE"`
    echo Script-Dir-Relative : `dirname $0`
    echo
    echo Calling-Dir : `pwd`

और मुझे यह आउटपुट मिला:

     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir

     Script-Name : test.sh
     Script-Name : test.sh

     Script-Dir-Relative : ..
     Script-Dir-Relative : ..

     Calling-Dir : /tmp/src dir/other

इसे भी देखें: https://pastebin.com/J8KjxrPF



मुझे लगता है कि मेरा जवाब ठीक है क्योंकि एक साधारण वर्किंग एडिशन मिलना मुश्किल है। यहां आप अपनी पसंद का कोड ले सकते हैं जैसे cd + pwd, dirname + realpath या dirname + readlink। मुझे यकीन नहीं है कि सभी भाग पहले से मौजूद हैं और अधिकांश उत्तर जटिल और अतिभारित हैं। यहां आप उस कोड को पाइक कर सकते हैं जिसे आप उपयोग करना चाहते हैं। कम से कम कृपया इसे भविष्य में मेरी ज़रूरत के अनुसार न निकालें: D
User8461

8

यह बाश-3.2 में काम करता है:

path="$( dirname "$( which "$0" )" )"

यदि आपके पास एक ~/binनिर्देशिका है $PATH, तो आप Aइस निर्देशिका के अंदर हैं। यह स्क्रिप्ट का स्रोत है ~/bin/lib/B। आपको पता है कि शामिल स्क्रिप्ट कहाँ मूल के सापेक्ष है, libउपनिर्देशिका में, लेकिन यह उपयोगकर्ता की वर्तमान निर्देशिका के सापेक्ष नहीं है।

यह निम्नलिखित (अंदर A) द्वारा हल किया गया है :

source "$( dirname "$( which "$0" )" )/lib/B"

इससे कोई फर्क नहीं पड़ता कि उपयोगकर्ता कहाँ है या वह स्क्रिप्ट को कैसे कॉल करता है, यह हमेशा काम करेगा।


3
इस पर whichबात बहुत बहस की है। type, hashऔर अन्य बिल्डिंग्स बैश में बेहतर काम करते हैं। whichयह अधिक पोर्टेबल है, हालांकि यह वास्तव whichमें tcsh जैसे अन्य गोले में उपयोग नहीं किया जाता है, कि यह एक buildin के रूप में है।
मोनिका प्लीज

"हमेशा"? हर्गिज नहीं। whichएक बाहरी उपकरण होने के नाते, आपके पास यह मानने का कोई कारण नहीं है कि यह माता-पिता के खोल के लिए समान व्यवहार करता है।
चार्ल्स डफी

7

मेरे विचार में सबसे अच्छा कॉम्पैक्ट समाधान होगा:

"$( cd "$( echo "${BASH_SOURCE[0]%/*}" )"; pwd )"

बाश के अलावा किसी और चीज पर निर्भरता नहीं है। का उपयोग dirname, readlinkऔर basenameअंततः संगतता मुद्दों को जन्म देगा, इसलिए यदि संभव हो तो उन्हें सबसे अच्छा टाला जाता है।


2
आपको संभवतः इसमें स्लैश जोड़ना चाहिए "$( cd "$( echo "${BASH_SOURCE[0]%/*}/" )"; pwd )":। यदि आप नहीं करते हैं तो आपको रूट डायरेक्टरी में समस्याएँ होंगी। इसके अलावा, आपको इको का उपयोग क्यों करना है?
konsolebox

dirnameऔर basenamePOSIX मानकीकृत हैं, तो उनका उपयोग करने से बचें क्यों? लिंक: dirname,basename
myrdd

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