यूनिक्स खोल स्क्रिप्ट यह पता लगाती है कि स्क्रिप्ट फ़ाइल किस निर्देशिका में रहती है?


510

मूल रूप से मुझे स्क्रिप्ट को शेल स्क्रिप्ट फ़ाइल स्थान से संबंधित पथ के साथ चलाने की आवश्यकता है, मैं वर्तमान निर्देशिका को उसी निर्देशिका में कैसे बदल सकता हूं जहां स्क्रिप्ट फ़ाइल रहती है?


12
क्या वह वास्तव में डुप्लिकेट है? यह सवाल एक "यूनिक्स खोल स्क्रिप्ट" के बारे में है, दूसरा विशेष रूप से बैश के बारे में।
माइकलजंट

2
@BoltClock: यह प्रश्न अनुचित रूप से बंद था। जुड़ा हुआ सवाल बैश के बारे में है। यह सवाल यूनिक्स शेल प्रोग्रामिंग के बारे में है। ध्यान दें कि स्वीकृत उत्तर बिल्कुल अलग हैं!
डिट्रीच एप ईपी

@ डिट्रिक एप: आप सही कह रहे हैं। ऐसा लगता है कि स्वीकार किए गए उत्तर के प्रश्नकर्ता की पसंद और [बैश] टैग के अतिरिक्त (शायद उसी के जवाब में) ने मुझे एक ध्वज के जवाब में डुप्लिकेट के रूप में प्रश्न चिह्नित करने के लिए प्रेरित किया।
BoltClock


जवाबों:


566

बैश में, आपको इस तरह की आवश्यकता होनी चाहिए:

#!/usr/bin/env bash

BASEDIR=$(dirname "$0")
echo "$BASEDIR"

17
यदि आपने स्क्रिप्ट को एक अलग निर्देशिका में प्रतीकात्मक लिंक के माध्यम से बुलाया है तो यह काम नहीं करता है। उस काम को करने के लिए आपको उपयोग करने की आवश्यकता है readlink(नीचे दिए गए उत्तर को देखें)
एंड्रयू २६

44
बैश में, इसके $BASH_SOURCEएवज में उपयोग करना अधिक सुरक्षित होता है $0, क्योंकि $0इसमें हमेशा स्क्रिप्ट के मार्ग को शामिल नहीं किया जाता है, जैसे कि किसी स्क्रिप्ट को 'सोर्सिंग' करते समय।
mklement0

2
$BASH_SOURCEबैश-विशिष्ट है, प्रश्न सामान्य रूप से शेल स्क्रिप्ट के बारे में है।
हा-डुओंग गुयेन

7
@ सौरम: CUR_PATH=$(pwd)या pwdवर्तमान निर्देशिका (जिसकी स्क्रिप्ट अभिभावक को नहीं होनी चाहिए) लौटाते हैं!
एंड्रियास डिट्रीच

5
मैंने विधि का उपयोग करने की कोशिश की @ mklement0 की, उपयोग करने की सिफारिश की $BASH_SOURCE, और यह वही देता है जो मुझे चाहिए था। मेरी स्क्रिप्ट को किसी अन्य स्क्रिप्ट से कॉल किया जा रहा है, और सही उपनिर्देशिका (मेरे मामले में ) $0रिटर्न .करते समय । $BASH_SOURCEscripts
डेविड रिसाटो क्रूज़

401

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

बाश, श, क्ष के लिए:

#!/bin/bash 
# Absolute path to this script, e.g. /home/user/bin/foo.sh
SCRIPT=$(readlink -f "$0")
# Absolute path this script is in, thus /home/user/bin
SCRIPTPATH=$(dirname "$SCRIPT")
echo $SCRIPTPATH

Tsh के लिए, csh:

#!/bin/tcsh
# Absolute path to this script, e.g. /home/user/bin/foo.csh
set SCRIPT=`readlink -f "$0"`
# Absolute path this script is in, thus /home/user/bin
set SCRIPTPATH=`dirname "$SCRIPT"`
echo $SCRIPTPATH

इसे भी देखें: https://stackoverflow.com/a/246128/59087


12
नोट: सभी प्रणालियों में नहीं है readlink। इसलिए मैंने पुशड / पॉपड (बैश के लिए बिल्ट-इन) का उपयोग करने की सिफारिश की।
docwhat

23
ओएस एक्स (लायन) और संभवतः बीएसडी पर कुछ अलग -fकरने का विकल्प readlinkstackoverflow.com/questions/1055671/…
Ergwun

9
@ एर्गवुन की टिप्पणी को स्पष्ट करने के लिए: -fओएस एक्स पर बिल्कुल भी समर्थित नहीं है (सिंह के रूप में); वहाँ आप या तो -fअप्रत्यक्ष स्तर के अधिकांश एक स्तर पर हल करने के लिए pushd "$(dirname "$(readlink "$BASH_SOURCE" || echo "$BASH_SOURCE")")"कर सकते हैं , उदाहरण के लिए , या आप अपनी खुद की पुनरावर्ती सिम्क्लिन-निम्नलिखित स्क्रिप्ट को लिंक की गई पोस्ट में प्रदर्शित कर सकते हैं।
mklement0

2
मुझे अभी भी समझ नहीं आ रहा है कि ओपी को निरपेक्ष पथ की आवश्यकता क्यों होगी। रिपोर्टिंग "।" यदि आप स्क्रिप्ट पथ के सापेक्ष फ़ाइलों तक पहुँचना चाहते हैं, तो आपको ठीक से काम करना चाहिए और आपने स्क्रिप्ट को कॉल किया। जैसे .myscript.sh
Stefan Haberl

10
@StefanHaberl मुझे लगता है कि यह एक मुद्दा होगा यदि आपने स्क्रिप्ट को चलाया, जबकि आपकी वर्तमान कार्यशील निर्देशिका स्क्रिप्ट के स्थान से अलग थी (उदाहरण के लिए sh /some/other/directory/script.sh), इस मामले में .आपका pwd होगा, न कि/some/other/directory
जॉन z

51

एक उत्तर पर पहले की टिप्पणी ने इसे कहा, लेकिन अन्य सभी उत्तरों में से याद करना आसान है।

बैश का उपयोग करते समय:

echo this file: "$BASH_SOURCE"
echo this dir: "$(dirname "$BASH_SOURCE")"

बैश संदर्भ मैनुअल, 5.2 बैश चर


3
केवल यह पर्यावरण के रास्ते में स्क्रिप्ट के साथ काम करता है, सबसे अधिक मतदान वाले काम नहीं करते हैं। धन्यवाद!
जुलाई

आपको dirname "$BASH_SOURCE"इसके बजाय $ BASH_SOURCE में रिक्त स्थान को संभालने के लिए उपयोग करना चाहिए ।
माईगॉड

1
टूल शेलचेक के अनुसार, निर्देशिका को प्रिंट करने का एक और अधिक स्पष्ट तरीका होगा: "$ (dirname" $ ​​{BASH_SOURCE {0}} ")" क्योंकि BASH_SOURCE एक सरणी है, और सबस्क्रिप्ट के बिना, पहला तत्व डिफ़ॉल्ट रूप से लिया जाता है। ।
एड्रियन एम।

1
@AdrianM। , आप कोष्ठक चाहते हैं, ब्रेसिज़ नहीं, सूचकांक के लिए:"$(dirname "${BASH_SOURCE[0]}")"
हैशबोर्न

मेरे लिए कोई अच्छा नहीं..बस एक बिंदु (यानी वर्तमान निर्देशिका, संभवतः)
JL_SO

40

मान लें कि आप बैश का उपयोग कर रहे हैं

#!/bin/bash

current_dir=$(pwd)
script_dir=$(dirname $0)

echo $current_dir
echo $script_dir

इस स्क्रिप्ट को उस निर्देशिका को प्रिंट करना चाहिए जो आप अंदर हैं, और फिर वह स्क्रिप्ट जिस निर्देशिका में है। उदाहरण के लिए, इसे /स्क्रिप्ट से कॉल करते समय /home/mez/, यह आउटपुट करता है

/
/home/mez

याद रखें, जब एक कमांड के आउटपुट से वेरिएबल्स को असाइन किया जाता है, तो कमांड को इन में लपेटें - $(और )- या आपको वांछित आउटपुट नहीं मिलेगा।


3
यह काम नहीं करेगा जब मैं स्क्रिप्ट को वर्तमान dir से आह्वान करता हूँ।
एरिक वांग

1
@EricWang आप हमेशा वर्तमान निर्देशिका में हैं।
ctrl-alt-delor-

मेरे लिए, $ current_dir वास्तव में पथ है जिसे मैं स्क्रिप्ट कह रहा हूं। हालाँकि, $ script_dir स्क्रिप्ट की डायर नहीं है, यह सिर्फ एक डॉट है।
माइकल

31

यदि आप बैश का उपयोग कर रहे हैं ...।

#!/bin/bash

pushd $(dirname "${0}") > /dev/null
basedir=$(pwd -L)
# Use "pwd -P" for the path without links. man bash for more info.
popd > /dev/null

echo "${basedir}"

4
आप बदल सकते हैं pushd/ popdके साथ cd $(dirname "${0}")और cd -यह अन्य गोले पर काम करते हैं, अगर वे एक है बनाने के लिए pwd -L
docwhat

आप यहां पुशड और पॉप का उपयोग क्यों करेंगे?
qodeninja

1
इसलिए मुझे एक चर में मूल निर्देशिका को संग्रहीत करने की आवश्यकता नहीं है। यह एक पैटर्न है जो मैं फ़ंक्शन और ऐसे में बहुत उपयोग करता हूं। यह वास्तव में अच्छी तरह से घोंसला बनाता है, जो अच्छा है।
docwhat

यह अभी भी मेमोरी में संग्रहीत किया जा रहा है - एक चर में - चाहे आपकी स्क्रिप्ट में एक चर संदर्भित हो या नहीं। इसके अलावा, मेरा मानना ​​है कि सीपीयू साइकिल और पठनीयता दोनों में, आपकी स्क्रिप्ट में एक स्थानीय बैश वैरिएबल नहीं बनाने की बचत से पुशड और पॉपड को निष्पादित करने की लागत बढ़ जाती है।
21

20

जैसा कि मैकार्को सुझाव देता है:

BASEDIR=$(dirname $0)
echo $BASEDIR

यह तब तक काम करता है जब तक आप स्क्रिप्ट को उसी डायरेक्टरी से निष्पादित नहीं करते हैं जहां स्क्रिप्ट रहती है, जिस स्थिति में आपको '' '' का मान मिलता है।

उस समस्या का उपयोग करने के लिए:

current_dir=$(pwd)
script_dir=$(dirname $0)

if [ $script_dir = '.' ]
then
script_dir="$current_dir"
fi

स्क्रिप्ट निर्देशिका को संदर्भित करने के लिए अब आप अपनी स्क्रिप्ट में चर current_dir का उपयोग कर सकते हैं। हालाँकि यह अभी भी सहानुभूति समस्या हो सकती है।


20

इस सवाल का सबसे अच्छा जवाब यहां दिया गया था:
बश लिपि के स्रोत निर्देशिका को भीतर से प्राप्त करना

और यह है:

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

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

यह समझने के लिए कि यह कैसे काम करता है आप निम्नलिखित स्क्रिप्ट निष्पादित कर सकते हैं:

#!/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" )" && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"


10

आइए इसे बनाते हैं POSIX ऑनलाइनर:

a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(cd "$a"; pwd)

बीएसडी वालों सहित कई बॉर्न-संगत गोले पर परीक्षण किया गया।

जहां तक ​​मुझे पता है कि मैं लेखक हूं और मैंने इसे सार्वजनिक डोमेन में डाल दिया है। अधिक जानकारी के लिए देखें: https://www.jasan.tk/posts/2017-05-11-posix_shell_dirname_nplaceplace/


1
के रूप में लिखा है, cd: too many argumentsअगर रास्ते में रिक्त स्थान, और रिटर्न $PWD। (एक स्पष्ट तय है, लेकिन अभी पता चलता है कि वास्तव में कितने किनारे मामले हैं)
माइकल

1
उखाड़ लेगा। @Michael की टिप्पणी को छोड़कर कि यह पथ में रिक्त स्थान के साथ विफल रहता है ... क्या इसके लिए कोई निर्धारण है?
6

1
@spechter हाँ, इसके लिए एक फिक्स है। अपडेट किया गया jasan.tk/posix/2017/05/11/posix_shell_dirname_replacement
JAN Sáreník

@Der_Meister - कृपया अधिक विशिष्ट बनें। या (संभवतः एन्क्रिप्ट) लिखने के लिए jasan.tk पर jasan को एक ईमेल
JAN Sáreník

2
ln -s / home / der / 1 / test / home / der / 2 / test और& / home / der / 2 / test => / home / der / 2 (इसके बजाय मूल स्क्रिप्ट को रास्ता दिखाना चाहिए)
Der_Meister

10
BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
echo "BASE_DIR => $BASE_DIR"

3
सबसे विश्वसनीय गैर-बैश-विशिष्ट तरीका जो मुझे पता है।
eddygeek

9

यदि आप वास्तविक स्क्रिप्ट डायरेक्टरी प्राप्त करना चाहते हैं (भले ही आप स्क्रिप्ट को सिम्कलिन या सीधे प्रयोग कर रहे हों), कोशिश करें:

BASEDIR=$(dirname $(realpath "$0"))
echo "$BASEDIR"

यह linux और macOS दोनों पर काम करता है। मैं किसी को भी यहाँ के बारे में उल्लेख नहीं देख सकता था realpath। सुनिश्चित नहीं हैं कि इस दृष्टिकोण में कोई कमियां हैं या नहीं।

macOS पर, आपको coreutilsउपयोग करने के लिए इंस्टॉल करने की आवश्यकता है realpath। उदाहरण के लिए: brew install coreutils


6

परिचय

यह उत्तर इस थ्रेड (TheMarko द्वारा लिखित) के बहुत ही टूटे-फूटे लेकिन चौंकाने वाले टॉप वोट किए गए उत्तर को सही करता है:

#!/usr/bin/env bash

BASEDIR=$(dirname "$0")
echo "$BASEDIR"

क्यों यह "$ 0" का उपयोग नहीं कर रहा है यह काम नहीं करता है?

dirname $ 0 केवल तभी काम करेगा जब उपयोगकर्ता स्क्रिप्ट को बहुत विशिष्ट तरीके से लॉन्च करेगा। मैं कई स्थितियों को खोजने में सक्षम था जहां यह उत्तर विफल हो जाता है और स्क्रिप्ट को क्रैश कर देता है।

सबसे पहले, आइए समझते हैं कि यह उत्तर कैसे काम करता है। वह स्क्रिप्ट डायरेक्टरी कर रहा है

dirname "$0"

$ 0 स्क्रिप्ट का आदेश देने वाले कमांड के पहले भाग का प्रतिनिधित्व करता है (यह मूल रूप से तर्कों के बिना इनपुट कमांड है:

/some/path/./script argument1 argument2

$ 0 = "/ कुछ / पथ /./ स्क्रिप्ट"

dirname मूल रूप से अंतिम / एक स्ट्रिंग में पाता है और वहां इसे काट देता है। तो अगर तुम करते हो:

  dirname /usr/bin/sha256sum

आपको मिलेगा: / usr / bin

यह उदाहरण अच्छी तरह से काम करता है क्योंकि / usr / bin / sha256sum एक उचित स्वरूपित पथ है लेकिन

  dirname "/some/path/./script"

अच्छा काम नहीं करेगा और आपको देगा:

  BASENAME="/some/path/." #which would crash your script if you try to use it as a path

कहते हैं कि आप अपनी स्क्रिप्ट के समान ही हैं और आप इसे इस आदेश के साथ लॉन्च करते हैं

./script   

इस स्थिति में $ 0 होगा ।/cript और dirname $ 0 देगा:

. #or BASEDIR=".", again this will crash your script

का उपयोग करते हुए:

sh script

पूरा रास्ता इनपुट किए बिना भी एक BASEDIR = "देगा।"

रिश्तेदार निर्देशिकाओं का उपयोग करना:

 ../some/path/./script

का dirname $ 0 देता है:

 ../some/path/.

यदि आप / कुछ निर्देशिका में हैं और आप इस तरीके से स्क्रिप्ट को कॉल करते हैं (शुरुआत में / फिर से एक रिश्तेदार पथ की अनुपस्थिति पर ध्यान दें):

 path/./script.sh

आपको dirname $ 0 के लिए यह मान मिलेगा:

 path/. 

और ./path/./cript (सापेक्ष पथ का दूसरा रूप) देता है:

 ./path/.

केवल दो स्थितियाँ जहाँ $ 0 काम करेगा, यदि उपयोगकर्ता किसी स्क्रिप्ट को लॉन्च करने के लिए श या टच का उपयोग करता है क्योंकि दोनों का परिणाम $ 0 होगा:

 $0=/some/path/script

जो आपको एक मार्ग देगा जिसका उपयोग आप dirname के साथ कर सकते हैं।

समाधान

आपके पास उपरोक्त उल्लिखित स्थितियों में से हर एक का पता लगाने और उसका पता लगाने और उसके लिए एक सुधार लागू करने की आवश्यकता होगी:

#!/bin/bash
#this script will only work in bash, make sure it's installed on your system.

#set to false to not see all the echos
debug=true

if [ "$debug" = true ]; then echo "\$0=$0";fi


#The line below detect script's parent directory. $0 is the part of the launch command that doesn't contain the arguments
BASEDIR=$(dirname "$0") #3 situations will cause dirname $0 to fail: #situation1: user launches script while in script dir ( $0=./script)
                                                                     #situation2: different dir but ./ is used to launch script (ex. $0=/path_to/./script)
                                                                     #situation3: different dir but relative path used to launch script
if [ "$debug" = true ]; then echo 'BASEDIR=$(dirname "$0") gives: '"$BASEDIR";fi                                 

if [ "$BASEDIR" = "." ]; then BASEDIR="$(pwd)";fi # fix for situation1

_B2=${BASEDIR:$((${#BASEDIR}-2))}; B_=${BASEDIR::1}; B_2=${BASEDIR::2}; B_3=${BASEDIR::3} # <- bash only
if [ "$_B2" = "/." ]; then BASEDIR=${BASEDIR::$((${#BASEDIR}-1))};fi #fix for situation2 # <- bash only
if [ "$B_" != "/" ]; then  #fix for situation3 #<- bash only
        if [ "$B_2" = "./" ]; then
                #covers ./relative_path/(./)script
                if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/${BASEDIR:2}"; else BASEDIR="/${BASEDIR:2}";fi
        else
                #covers relative_path/(./)script and ../relative_path/(./)script, using ../relative_path fails if current path is a symbolic link
                if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/$BASEDIR"; else BASEDIR="/$BASEDIR";fi
        fi
fi

if [ "$debug" = true ]; then echo "fixed BASEDIR=$BASEDIR";fi

4

यह एक-लाइनर बताता है कि शेल स्क्रिप्ट कहां है, इससे कोई फर्क नहीं पड़ता कि आप इसे चलाते हैं या यदि आपने इसे खट्टा किया है । इसके अलावा, यह किसी भी प्रतीकात्मक लिंक को हल करता है, अगर ऐसा है:

dir=$(dirname $(test -L "$BASH_SOURCE" && readlink -f "$BASH_SOURCE" || echo "$BASH_SOURCE"))

वैसे, मुझे लगता है कि आप / बिन / बैश का उपयोग कर रहे हैं ।


2

इतने सारे उत्तर, सभी प्रशंसनीय, प्रत्येक समर्थक और चोर के & थोड़े अलग उद्देश्यों के साथ (जो शायद प्रत्येक के लिए कहा जाना चाहिए)। यहाँ एक और समाधान है जो सभी प्रणालियों पर सभी स्पष्ट और सभी प्रणालियों पर काम करने का एक प्राथमिक उद्देश्य पूरा करता है, (bash संस्करणों के बारे में कोई धारणा नहीं), या readlinkयाpwd विकल्प के करता है, और यथोचित रूप से वही होता है जो आप होने की उम्मीद करते हैं (उदाहरण के लिए, सहानुभूति का समाधान करना एक है) दिलचस्प समस्या है, लेकिन आम तौर पर आप जो चाहते हैं वह नहीं होता है), पथ के मामलों में बढ़त जैसे मामलों को संभालते हैं, आदि, किसी भी त्रुटि को अनदेखा करता है और कोई समस्या होने पर एक डिफ़ॉल्ट डिफ़ॉल्ट का उपयोग करता है।

प्रत्येक घटक को एक अलग चर में संग्रहीत किया जाता है जिसे आप व्यक्तिगत रूप से उपयोग कर सकते हैं:

# script path, filename, directory
PROG_PATH=${BASH_SOURCE[0]}      # this script's name
PROG_NAME=${PROG_PATH##*/}       # basename of script (strip path)
PROG_DIR="$(cd "$(dirname "${PROG_PATH:-$PWD}")" 2>/dev/null 1>&2 && pwd)"


-4

यह ट्रिक काम आना चाहिए:

echo `pwd`/`dirname $0`

यह बदसूरत लग सकता है कि यह कैसे लागू किया गया था और सीडब्ल्यूडी लेकिन आपको वहां जाना चाहिए जहां आपको जाने की आवश्यकता है (या आप स्ट्रिंग को ट्विक कर सकते हैं यदि आप परवाह करते हैं कि यह कैसा दिखता है)।


1
स्टैकओवरफ्लो भागने की समस्या यहाँ: यह निश्चित रूप से इस तरह दिखना चाहिए: `pwd`/`dirname $0`लेकिन अभी भी सहानुभूति पर विफल हो सकता है
एंड्रियास डिट्रिच
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.