एक शेल स्क्रिप्ट में प्रतीकात्मक लिंक कैसे हल करें


220

एक निरपेक्ष या सापेक्ष पथ (एक यूनिक्स जैसी प्रणाली में) को देखते हुए, मैं किसी भी मध्यवर्ती सिमलिंक को हल करने के बाद लक्ष्य का पूरा रास्ता निर्धारित करना चाहूंगा। एक ही समय में ~ उपयोगकर्ता नामकरण को हल करने के लिए बोनस अंक भी।

यदि लक्ष्य एक निर्देशिका है, तो यह निर्देशिका में chdir () और फिर getcwd () को कॉल करना संभव हो सकता है, लेकिन मैं वास्तव में C सहायक लिखने के बजाय शेल स्क्रिप्ट से ऐसा करना चाहता हूं। दुर्भाग्य से, शेल में उपयोगकर्ता से सीमलिंक के अस्तित्व को छिपाने की कोशिश की जाती है (यह ओएस एक्स पर बैश है):

$ ls -ld foo bar
drwxr-xr-x   2 greg  greg  68 Aug 11 22:36 bar
lrwxr-xr-x   1 greg  greg   3 Aug 11 22:36 foo -> bar
$ cd foo
$ pwd
/Users/greg/tmp/foo
$

मैं जो चाहता हूं वह एक फ़ंक्शन रिज़ॉल्यूशन () ऐसा है जो उपरोक्त उदाहरण में tmp निर्देशिका से निष्पादित होने पर, हल ("फू") == "/ उपयोगकर्ता / greg / tmp / बार"।

जवाबों:


92

मानकों के अनुसार, pwd -P हल किए गए सिम्बल के साथ पथ को वापस करना चाहिए।

C फ़ंक्शन char *getcwd(char *buf, size_t size)से unistd.hसमान व्यवहार होना चाहिए।

getcwd pwd


22
यह सिर्फ (वर्तमान) निर्देशिका के लिए काम करता है? यदि लक्ष्य एक फ़ाइल है, तो यह कुछ भी नहीं देगा ...
dala

4
लिंक टूट जाने पर काम नहीं करता है क्योंकि आप इसे अपना वर्तमान पथ नहीं बना सकते हैं
टॉम हावर्ड

6
दृष्टि के लाभ के साथ संक्षेप में प्रस्तुत करने के लिए: यह उत्तर केवल बहुत ही सीमित परिस्थितियों में काम करता है, अर्थात् यदि ब्याज की सहानुभूति एक निर्देशिका के लिए है जो वास्तव में मौजूद है ; इसके अलावा, आपको cdकॉल करने से पहले इसे पहले करना होगा pwd -P। दूसरे शब्दों में: यह आपको फ़ाइलों के लिए या टूटी हुई सीमलिंक के लिए (लक्ष्य को देखने के लिए) सिमिलिंक को हल करने की अनुमति नहीं देगा , और मौजूदा-डायरेक्टरी सिर्लिंक को हल करने के लिए आपको अतिरिक्त काम करना होगा (पिछले काम करने वाले डायर को पुनर्स्थापित करें या कॉल cdऔर स्थानीयकरण करें) pwd -Pएक उपखंड में)।
mklement0

खराब करने के लिए, मैं एक फ़ाइल को हल करने के लिए एक रास्ता खोजता हूं और निर्देशिका नहीं।
मार्टिन

जैसा कि दूसरों ने बताया है, यह वास्तव में सवाल का जवाब नहीं देता है। @ पिक्सेलबीट का जवाब नीचे दिया गया है।
erstaples

402
readlink -f "$path"

संपादक का नोट: उपरोक्त GNU readlink और FreeBSD / PC-BSD / OpenBSD के साथ काम करता है readlink, लेकिन OS X पर 10.11 के रूप में नहीं
GNU readlink अतिरिक्त, संबंधित विकल्प प्रदान करता है, जैसे -mकि सिमलिंक को हल करने के लिए कि अंतिम लक्ष्य मौजूद है या नहीं।

नोट: जीएनयू कोरुटिल्स 8.15 (2012-01-06) के बाद से, वहाँ एक रियलपैथ प्रोग्राम उपलब्ध है जो कम ओब्यूट और ऊपर से अधिक लचीला है। यह उसी नाम के FreeBSD उपयोग के साथ भी संगत है। इसमें दो फ़ाइलों के बीच एक सापेक्ष पथ उत्पन्न करने के लिए कार्यक्षमता भी शामिल है।

realpath $path

[ हललो द्वारा टिप्पणी के अलावा नीचे व्यवस्थापक - danorton]

मैक ओएस एक्स के लिए (कम से कम 10.11.x के माध्यम से), विकल्प के readlinkबिना उपयोग करें -f:

readlink $path

संपादक का ध्यान दें: यह पुनरावर्ती रूप से सीलिंक को हल नहीं करेगा और इस प्रकार अंतिम लक्ष्य की सूचना नहीं देगा ; उदाहरण के लिए, सिमलिंक दिया गया है aजो इंगित करता है b, जो बदले में इंगित करता है c, यह केवल रिपोर्ट bकरेगा (और यह सुनिश्चित नहीं करेगा कि यह एक पूर्ण पथ के रूप में आउटपुट है )। गुम कार्यक्षमता की खाई को भरने के लिए OS X पर
निम्न perlकमांड का उपयोग करें readlink -f:
perl -MCwd -le 'print Cwd::abs_path(shift)' "$path"


5
यह मैक ओएस एक्स में काम नहीं करता है - देखें stackoverflow.com/questions/1055671/…
Bkkbrad

1
OS X असंगति के बारे में शर्म की बात है, अन्यथा अच्छा +1
jkp

11
ओएस एक्स पर आप होमब्रे के साथ कोरयूटिल्स स्थापित कर सकते हैं। यह इसे "grealpath" के रूप में स्थापित करता है।
केफ

10
readlinkपर OSX काम करता है, लेकिन एक और वाक्य रचना की जरूरत है: readlink $path बिना-f
हलालू

2
सिमिलर की कई परतों को डी-रेफ़्लिफ़ करने में विफल रहता है, हालांकि, यह एक बार में केवल एक परत को निष्क्रिय कर देता है
Magnus

26

अगर आप सिर्फ डायरेक्टरी चाहते हैं तो "pwd -P" काम करता है, लेकिन अगर किसी कारण से आप वास्तविक निष्पादन योग्य का नाम चाहते हैं तो मुझे नहीं लगता कि इससे मदद मिलती है। यहाँ मेरा समाधान है:

#!/bin/bash

# get the absolute path of the executable
SELF_PATH=$(cd -P -- "$(dirname -- "$0")" && pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "$0")

# resolve symlinks
while [[ -h $SELF_PATH ]]; do
    # 1) cd to directory of the symlink
    # 2) cd to the directory of where the symlink points
    # 3) get the pwd
    # 4) append the basename
    DIR=$(dirname -- "$SELF_PATH")
    SYM=$(readlink "$SELF_PATH")
    SELF_PATH=$(cd "$DIR" && cd "$(dirname -- "$SYM")" && pwd)/$(basename -- "$SYM")
done

17

मेरा एक पसंदीदा है realpath foo

realpath - canonicalized निरपेक्ष पथनाम वापस करें

realpath सभी प्रतीकात्मक लिंक का विस्तार करता है और '/ //', '/../' और अतिरिक्त '/' वर्णों को मार्ग द्वारा नामित शून्य समाप्त स्ट्रिंग में संदर्भों को हल करता है और
       हल किए गए PATH_MAX आकार के बफ़र में canonicalized निरपेक्ष पथनाम को स्टोर किया जाता है, जिसे solution_path नाम दिया गया है। परिणामस्वरूप पथ का कोई प्रतीकात्मक लिंक नहीं होगा, '/' / 'या
       '/../' अवयव।

डेबियन (etch और बाद में) पर यह कमांड realpath पैकेज में उपलब्ध है।
फिल रॉस

2
realpath अब (जनवरी 2012)
कोर का

1
मैं realpathGNU Coreutils 8.4.31 के साथ Centos 6 पर नहीं है । मैं यूनिक्स और लिनक्स पर कई अन्य लोगों के सामने आया हूं जिनके पास बिना जीएनयू कोरुटिल पैक है realpath। तो यह सिर्फ संस्करण से अधिक पर निर्भर होने लगता है।
टॉक्सालॉट

मैं पसंद realpathसे अधिक readlinkहै क्योंकि यह इस तरह के रूप विकल्प झंडे प्रदान करता है--relative-to
arr_sea

10
readlink -e [filepath]

जैसा आप पूछ रहे हैं वैसा ही प्रतीत होता है - यह एक अभिमानी मार्ग को स्वीकार करता है, सभी सिम्लिंक को हल करता है, और "वास्तविक" पथ को लौटाता है - और यह "मानक * निक्स" है, जिसकी संभावना सभी प्रणालियों में पहले से ही है


बस इससे काट गया। यह मैक पर काम नहीं करता है और मैं प्रतिस्थापन के लिए खोज रहा हूं।
ढिल

5

दूसरा रास्ता:

# Gets the real path of a link, following all links
myreadlink() { [ ! -h "$1" ] && echo "$1" || (local link="$(expr "$(command ls -ld -- "$1")" : '.*-> \(.*\)$')"; cd $(dirname $1); myreadlink "$link" | sed "s|^\([^/].*\)\$|$(dirname $1)/\1|"); }

# Returns the absolute path to a command, maybe in $PATH (which) or not. If not found, returns the same
whereis() { echo $1 | sed "s|^\([^/].*/.*\)|$(pwd)/\1|;s|^\([^/]*\)$|$(which -- $1)|;s|^$|$1|"; } 

# Returns the realpath of a called command.
whereis_realpath() { local SCRIPT_PATH=$(whereis $1); myreadlink ${SCRIPT_PATH} | sed "s|^\([^/].*\)\$|$(dirname ${SCRIPT_PATH})/\1|"; } 

मुझे myreadlink () में cd की आवश्यकता है, क्योंकि यह एक पुनरावर्ती कार्य है, प्रत्येक निर्देशिका में जा रहा है जब तक यह एक लिंक नहीं पाता है। यदि यह एक लिंक ढूंढता है, तो रियलपथ लौटाता है, और फिर sed पथ को बदल देगा।
कीमोन

5

दिए गए कुछ समाधानों को एक साथ रखते हुए, यह जानते हुए कि अधिकांश प्रणालियों पर रीडलिंक उपलब्ध है, लेकिन विभिन्न तर्कों की आवश्यकता है, यह OSX और डेबियन पर मेरे लिए अच्छा काम करता है। मैं बीएसडी सिस्टम के बारे में निश्चित नहीं हूं। हो सकता है कि हालत को केवल OSX से [[ $OSTYPE != darwin* ]]बाहर करने की आवश्यकता हो -f

#!/bin/bash
MY_DIR=$( cd $(dirname $(readlink `[[ $OSTYPE == linux* ]] && echo "-f"` $0)) ; pwd -P)
echo "$MY_DIR"

3

यहाँ बताया गया है कि कोई इनलाइन पर्ल स्क्रिप्ट का उपयोग करके MacOS / Unix में फ़ाइल का वास्तविक पथ कैसे प्राप्त कर सकता है:

FILE=$(perl -e "use Cwd qw(abs_path); print abs_path('$0')")

इसी प्रकार, एक सिम्लिंक की गई फ़ाइल की निर्देशिका प्राप्त करने के लिए:

DIR=$(perl -e "use Cwd qw(abs_path); use File::Basename; print dirname(abs_path('$0'))")

3

क्या आपका मार्ग एक निर्देशिका है, या यह एक फ़ाइल हो सकती है? यदि यह एक निर्देशिका है, तो यह सरल है:

(cd "$DIR"; pwd -P)

हालाँकि, यदि यह एक फ़ाइल हो सकती है, तो यह काम नहीं करेगा:

DIR=$(cd $(dirname "$FILE"); pwd -P); echo "${DIR}/$(readlink "$FILE")"

क्योंकि सिम्लिंक एक रिश्तेदार या पूर्ण पथ में हल हो सकता है।

स्क्रिप्ट पर मुझे वास्तविक रास्ता खोजने की आवश्यकता है, ताकि मैं कॉन्फ़िगरेशन या अन्य स्क्रिप्ट को एक साथ स्थापित कर सकूं, मैं इसका उपयोग करता हूं:

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" && 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

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


2
function realpath {
    local r=$1; local t=$(readlink $r)
    while [ $t ]; do
        r=$(cd $(dirname $r) && cd $(dirname $t) && pwd -P)/$(basename $t)
        t=$(readlink $r)
    done
    echo $r
}

#example usage
SCRIPT_PARENT_DIR=$(dirname $(realpath "$0"))/..

यह (ए) व्हाट्सएप या शेल मेटाचैकर्स और (बी) टूटे हुए सिम्लिंक्स वाले किसी भी पथ के साथ टूट जाएगा (यदि आप सिर्फ स्क्रिप्ट चलाने के मूल मार्ग को चाहते हैं तो कोई समस्या नहीं है, यह मानते हुए कि (ए) लागू नहीं होता है)।
mklement0

यदि आप व्हाट्सएप के साथ संबंधित हैं तो उद्धरणों का उपयोग करें।
डेव

कृपया करें - हालांकि यह पता नहीं होगा (बी)।
mklement0

कृपया मुझे b का उदाहरण दिखाएं) जहां यह विफल रहता है। परिभाषा के अनुसार, एक टूटी हुई सिमिलिंक एक निर्देशिका प्रविष्टि की ओर इशारा करती है जो मौजूद नहीं है। इस लिपि का बिंदु दूसरी दिशा में सहानुभूति को हल करना है। यदि सिम्लिंक टूट गया था तो आप स्क्रिप्ट निष्पादित नहीं करेंगे। यह उदाहरण वर्तमान में निष्पादित स्क्रिप्ट के समाधान को प्रदर्शित करने के लिए है।
डेव

"वर्तमान में निष्पादित स्क्रिप्ट को हल करने का प्रदर्शन करने का इरादा है" - वास्तव में, जो आपके द्वारा चुने गए सवाल के दायरे का एक संकीर्णता है; यह पूरी तरह से ठीक है, जब तक आप ऐसा कहते हैं। चूंकि आपने नहीं किया, इसलिए मैंने इसे अपनी टिप्पणी में कहा। कृपया उद्धरण चिह्नों को ठीक करें, जो उत्तर के दायरे के बावजूद एक समस्या है।
mklement0

2

नोट: मेरा मानना ​​है कि यह एक ठोस, पोर्टेबल, रेडी-मेड समाधान है, जो कि बहुत ही कारण से काफी लंबा है।

नीचे एक पूरी तरह से POSIX- अनुरूप स्क्रिप्ट / फ़ंक्शन है, इसलिए यह क्रॉस-प्लेटफ़ॉर्म (macOS पर भी काम करता है, जिसका readlinkअभी भी -f10.12 (सिएरा) के रूप में समर्थन नहीं करता है ) - यह केवल POSIX शेल भाषा सुविधाओं और केवल POSIX- कंपाइलेंट उपयोगिता कॉल का उपयोग करता है ।

यह GNU'sreadlink -e (सख्त संस्करण readlink -f) का एक पोर्टेबल कार्यान्वयन है

आप कर सकते हैं चलाने स्क्रिप्ट के साथsh या स्रोत समारोह में bash, kshऔरzsh :

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

trueScriptDir=$(dirname -- "$(rreadlink "$0")")

rreadlink स्क्रिप्ट / फ़ंक्शन परिभाषा:

इस उत्तर से कोड को आभार के साथ अनुकूलित किया गया था ।
मैंने यहां एक bash-आधारित स्टैंड-अलोन यूटिलिटी वर्जन भी बनाया है , जिसे आप
npm install rreadlink -gNode.js स्थापित होने पर स्थापित कर सकते हैं ।

#!/bin/sh

# SYNOPSIS
#   rreadlink <fileOrDirPath>
# DESCRIPTION
#   Resolves <fileOrDirPath> to its ultimate target, if it is a symlink, and
#   prints its canonical path. If it is not a symlink, its own canonical path
#   is printed.
#   A broken symlink causes an error that reports the non-existent target.
# LIMITATIONS
#   - Won't work with filenames with embedded newlines or filenames containing 
#     the string ' -> '.
# COMPATIBILITY
#   This is a fully POSIX-compliant implementation of what GNU readlink's
#    -e option does.
# EXAMPLE
#   In a shell script, use the following to get that script's true directory of origin:
#     trueScriptDir=$(dirname -- "$(rreadlink "$0")")
rreadlink() ( # Execute the function in a *subshell* to localize variables and the effect of `cd`.

  target=$1 fname= targetDir= CDPATH=

  # Try to make the execution environment as predictable as possible:
  # All commands below are invoked via `command`, so we must make sure that
  # `command` itself is not redefined as an alias or shell function.
  # (Note that command is too inconsistent across shells, so we don't use it.)
  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not 
  # even have an external utility version of it (e.g, Ubuntu).
  # `command` bypasses aliases and shell functions and also finds builtins 
  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for
  # that to happen.
  { \unalias command; \unset -f command; } >/dev/null 2>&1
  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.

  while :; do # Resolve potential symlinks until the ultimate target is found.
      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
      fname=$(command basename -- "$target") # Extract filename.
      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
      if [ -L "$fname" ]; then
        # Extract [next] target path, which may be defined
        # *relative* to the symlink's own directory.
        # Note: We parse `ls -l` output to find the symlink target
        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
        target=$(command ls -l "$fname")
        target=${target#* -> }
        continue # Resolve [next] symlink target.
      fi
      break # Ultimate target reached.
  done
  targetDir=$(command pwd -P) # Get canonical dir. path
  # Output the ultimate target's canonical path.
  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
  if [ "$fname" = '.' ]; then
    command printf '%s\n' "${targetDir%/}"
  elif  [ "$fname" = '..' ]; then
    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
    # AFTER canonicalization.
    command printf '%s\n' "$(command dirname -- "${targetDir}")"
  else
    command printf '%s\n' "${targetDir%/}/$fname"
  fi
)

rreadlink "$@"

सुरक्षा पर एक स्पर्श:

jarno , फ़ंक्शन के संदर्भ में यह सुनिश्चित करता है कि बिलिनcommand एक ही नाम के उपनाम या शेल फ़ंक्शन द्वारा छायांकित नहीं है, एक टिप्पणी में पूछता है:

क्या होगा यदि unaliasया unsetऔर [उपनाम या खोल कार्यों के रूप में स्थापित कर रहे हैं?

यह rreadlinkसुनिश्चित करने के पीछे कि commandइसका मूल अर्थ यह है कि इसका उपयोग बाईपास (सौम्य) सुविधा के अलायस और फ़ंक्शंस के लिए किया जाता है, जो अक्सर lsपसंदीदा गोले को शामिल करने के लिए पुनर्परिभाषित करने जैसे इंटरैक्टिव शेल में मानक कमांड को छाया करने के लिए उपयोग किया जाता है ।

मुझे लगता है यह कहना है कि किसी अविश्वसनीय, दुर्भावनापूर्ण वातावरण के साथ हैं निपटने जब तक आप, के बारे में चिंता सुरक्षित है unaliasया unset- या, उस बात के लिए, while, doएक चिंता का विषय है नए सिरे से परिभाषित नहीं किया जा रहा -, ...।

ऐसा कुछ है जिसे फ़ंक्शन को अपने मूल अर्थ और व्यवहार पर भरोसा करना चाहिए - इसके आसपास कोई रास्ता नहीं है।
POSIX की तरह के गोले बिल्डरों को पुनर्परिभाषित करने की अनुमति देते हैं और यहां तक ​​कि भाषा के कीवर्ड स्वाभाविक रूप से एक सुरक्षा जोखिम है (और पैरानॉयड कोड लिखना सामान्य रूप से कठिन है)।

अपनी चिंताओं को विशेष रूप से संबोधित करने के लिए:

समारोह पर निर्भर करता है unaliasऔर unsetउनके मूल अर्थ है। उन्हें शेल के रूप में पुनर्परिभाषित करना इस तरह से कार्य करता है कि उनके व्यवहार में एक समस्या आएगी; एक के रूप में परिभाषा उर्फ जरूरी नहीं कि एक चिंता का विषय है, क्योंकि है हवाले से (का हिस्सा) आदेश नाम (जैसे, \unalias) उपनाम नजरअंदाज।

हालांकि, के हवाले से है नहीं खोल के लिए एक विकल्प कीवर्ड ( while, for, if, do, ...) और खोल कीवर्ड खोल से प्राथमिकता दी जाती है जबकि कार्यों , में bashऔर zshउपनाम तो खोल-कीवर्ड redefinitions आप चलाना चाहिए के खिलाफ गार्ड को, सर्वोच्च प्राथमिकता है unaliasसाथ उनके नाम (हालांकि गैर-संवादात्मक bash गोले में (जैसे लिपियों के रूप में) उपनाम डिफ़ॉल्ट रूप से विस्तारित नहीं होते हैं - केवल अगर shopt -s expand_aliasesस्पष्ट रूप से पहले कहा जाता है)।

यह सुनिश्चित करने के लिए कि unalias- एक बिलियन के रूप में - इसका मूल अर्थ है, आपको \unsetपहले उस पर उपयोग करना होगा , जिसके लिए unsetइसके मूल अर्थ की आवश्यकता होती है :

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

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


मेरे अनुभव में, आप पहले बताएं, इसके विपरीत, शेल कार्यों को नहीं, अलिज़ को उद्धृत करते हैं।
जारो

मैंने परीक्षण किया कि मैं [एक उपनाम के रूप में dashऔर bashएक शेल फ़ंक्शन के रूप में परिभाषित कर सकता हूं bash
जारो

1
मैंने एक अलग प्रश्न के रूप में अपनी बात रखी
जारो

@ कर्णो: अच्छे अंक; मैंने अपना उत्तर अपडेट कर दिया है; अगर आपको लगता है कि कोई समस्या है तो मुझे बताएं।
mklement0

1
@jarno: आप एक फ़ंक्शन के रूप में , और (लेकिन नहीं ) में परिभाषित कर सकते हैं , लेकिन केवल सिंटैक्स के साथ : काम करता है ( नहीं)। हालांकि, यह अभ्यस्त कीवर्ड को छाया करता है , क्योंकि कीवर्ड में फ़ंक्शंस की तुलना में अधिक पूर्वता है (इस फ़ंक्शन को लागू करने का एकमात्र तरीका है )। में और , उपनाम कीवर्ड की तुलना में अधिक पूर्वता, इसलिए है उर्फ कीवर्ड की redefinitions (लेकिन में उन्हें शैडो करते ही में डिफ़ॉल्ट रूप से इंटरैक्टिव , गोले, जब तक स्पष्ट रूप से कहा जाता है)। whilebashkshzshdashfunction <name>function while { echo foo; }while() { echo foo; }while \whilebashzshbashshopt -s expand_aliases
mklement0

1

सामान्य शेल स्क्रिप्ट को अक्सर अपने "होम" डायरेक्टरी को ढूंढना पड़ता है, भले ही वे एक सिमलिंक के रूप में आमंत्रित हों। इस प्रकार स्क्रिप्ट को केवल $ 0 से अपनी "वास्तविक" स्थिति ढूंढनी है।

cat `mvn`

मेरे सिस्टम में एक स्क्रिप्ट होती है जिसमें निम्नलिखित होते हैं, जो आपको चाहिए एक अच्छा संकेत होना चाहिए।

if [ -z "$M2_HOME" ] ; then
  ## resolve links - $0 may be a link to maven's home
  PRG="$0"

  # need this for relative symlinks
  while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
      PRG="$link"
    else
      PRG="`dirname "$PRG"`/$link"
    fi
  done

  saveddir=`pwd`

  M2_HOME=`dirname "$PRG"`/..

  # make it fully qualified
  M2_HOME=`cd "$M2_HOME" && pwd`

1

इसे इस्तेमाल करे:

cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))

1

चूँकि मैंने पिछले कई वर्षों में इसे चलाया है, और इस बार मुझे शुद्ध bash पोर्टेबल संस्करण की आवश्यकता थी जिसका उपयोग मैं OSX और linux पर कर सकता था, मैंने आगे बढ़कर एक लिखा:

जीवित संस्करण यहां रहता है:

https://github.com/keen99/shell-functions/tree/master/resolve_path

लेकिन एसओ के लिए, यहाँ वर्तमान संस्करण है (मुझे लगता है कि यह अच्छी तरह से परीक्षण किया गया है..लेकिन मैं प्रतिक्रिया के लिए खुला हूँ!)

यह सादे बोर्न शेल (श) के लिए काम करना मुश्किल नहीं हो सकता है, लेकिन मैंने कोशिश नहीं की ... मुझे $ FUNCNAME बहुत पसंद है। :)

#!/bin/bash

resolve_path() {
    #I'm bash only, please!
    # usage:  resolve_path <a file or directory> 
    # follows symlinks and relative paths, returns a full real path
    #
    local owd="$PWD"
    #echo "$FUNCNAME for $1" >&2
    local opath="$1"
    local npath=""
    local obase=$(basename "$opath")
    local odir=$(dirname "$opath")
    if [[ -L "$opath" ]]
    then
    #it's a link.
    #file or directory, we want to cd into it's dir
        cd $odir
    #then extract where the link points.
        npath=$(readlink "$obase")
        #have to -L BEFORE we -f, because -f includes -L :(
        if [[ -L $npath ]]
         then
        #the link points to another symlink, so go follow that.
            resolve_path "$npath"
            #and finish out early, we're done.
            return $?
            #done
        elif [[ -f $npath ]]
        #the link points to a file.
         then
            #get the dir for the new file
            nbase=$(basename $npath)
            npath=$(dirname $npath)
            cd "$npath"
            ndir=$(pwd -P)
            retval=0
            #done
        elif [[ -d $npath ]]
         then
        #the link points to a directory.
            cd "$npath"
            ndir=$(pwd -P)
            retval=0
            #done
        else
            echo "$FUNCNAME: ERROR: unknown condition inside link!!" >&2
            echo "opath [[ $opath ]]" >&2
            echo "npath [[ $npath ]]" >&2
            return 1
        fi
    else
        if ! [[ -e "$opath" ]]
         then
            echo "$FUNCNAME: $opath: No such file or directory" >&2
            return 1
            #and break early
        elif [[ -d "$opath" ]]
         then 
            cd "$opath"
            ndir=$(pwd -P)
            retval=0
            #done
        elif [[ -f "$opath" ]]
         then
            cd $odir
            ndir=$(pwd -P)
            nbase=$(basename "$opath")
            retval=0
            #done
        else
            echo "$FUNCNAME: ERROR: unknown condition outside link!!" >&2
            echo "opath [[ $opath ]]" >&2
            return 1
        fi
    fi
    #now assemble our output
    echo -n "$ndir"
    if [[ "x${nbase:=}" != "x" ]]
     then
        echo "/$nbase"
    else 
        echo
    fi
    #now return to where we were
    cd "$owd"
    return $retval
}

यहाँ एक उत्कृष्ट उदाहरण है, काढ़ा करने के लिए धन्यवाद:

%% ls -l `which mvn`
lrwxr-xr-x  1 draistrick  502  29 Dec 17 10:50 /usr/local/bin/mvn@ -> ../Cellar/maven/3.2.3/bin/mvn

इस फ़ंक्शन का उपयोग करें और यह -real- पथ लौटेगा:

%% cat test.sh
#!/bin/bash
. resolve_path.inc
echo
echo "relative symlinked path:"
which mvn
echo
echo "and the real path:"
resolve_path `which mvn`


%% test.sh

relative symlinked path:
/usr/local/bin/mvn

and the real path:
/usr/local/Cellar/maven/3.2.3/libexec/bin/mvn 

मेरे पहले के उत्तर में अंतरिक्ष के लगभग 1/4 भाग में एक ही बात होती है :) यह बहुत ही मूल शेल स्क्रिप्टिंग है और एक git रेपो के योग्य नहीं है।
डेव

1

मैक असंगतता के आसपास काम करने के लिए, मैं साथ आया था

echo `php -r "echo realpath('foo');"`

महान नहीं, लेकिन ओएस पार


3
Python 2.6+, php की तुलना में अधिक एंड-यूज़र सिस्टम पर उपलब्ध है, इसलिए python -c "from os import path; print(path.realpath('${SYMLINK_PATH}'));"संभवतः अधिक समझ में आएगा। फिर भी, जब तक आपको शेल स्क्रिप्ट से पायथन का उपयोग करने की आवश्यकता होती है, तब तक आपको शायद केवल पायथन का उपयोग करना चाहिए और अपने आप को क्रॉस प्लेटफॉर्म शेल स्क्रिप्टिंग के सिरदर्द से बचाना चाहिए।
जोनाथन बाल्डविन

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

@Dave: dirname, basename, और readlinkबाहरी हैं उपयोगिताओं , निर्मित-इन शेल नहीं; dirnameऔर basenamePOSIX का हिस्सा हैं, readlinkनहीं है।
mklement0

@ mklement0 - आप काफी सही हैं। वे CoreUtils या समकक्ष द्वारा प्रदान की जाती हैं। मुझे 1 बजे के बाद एसओ नहीं जाना चाहिए। मेरी टिप्पणी का सार यह है कि आधार प्रणाली में स्थापित न तो PHP और न ही किसी अन्य भाषा दुभाषिया से परे की आवश्यकता है। मैंने 1997 के बाद से हर लाइन वेरिएंट पर इस पेज पर दी गई स्क्रिप्ट का इस्तेमाल किया है और 2006 के बाद से MacOS X बिना किसी त्रुटि के। OP ने POSIX समाधान नहीं मांगा। उनका विशिष्ट वातावरण मैक ओएस एक्स था
डेव

@Dave: हाँ, यह है शेयर उपयोगिताओं के साथ यह करने के लिए संभव है, लेकिन यह भी है मुश्किल (के रूप में अपनी स्क्रिप्ट की कमियों इसका सबूत) ऐसा करने के लिए। अगर ओएस एक्स सही मायने में ध्यान केंद्रित थे, तो क्या यह उत्तर है बहुत सरल और - - यह देखते हुए कि बिल्कुल ठीक php, ओएस एक्स के साथ आता है हालांकि भले ही शरीर सवाल का उल्लेख ओएस एक्स, यह इस तरह के रूप में चिह्नित नहीं है, और यह स्पष्ट हो रहा है विभिन्न प्लेटफार्मों पर लोग उत्तर के लिए यहां आते हैं, इसलिए यह इंगित करने योग्य है कि प्लेटफॉर्म-विशिष्ट / गैर-पॉसिक्स क्या है।
mklement0

1

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

function readlinks {(
  set -o errexit -o nounset
  declare n=0 limit=1024 link="$1"

  # If it's a directory, just skip all this.
  if cd "$link" 2>/dev/null
  then
    pwd -P
    return 0
  fi

  # Resolve until we are out of links (or recurse too deep).
  while [[ -L $link ]] && [[ $n -lt $limit ]]
  do
    cd "$(dirname -- "$link")"
    n=$((n + 1))
    link="$(readlink -- "${link##*/}")"
  done
  cd "$(dirname -- "$link")"

  if [[ $n -ge $limit ]]
  then
    echo "Recursion limit ($limit) exceeded." >&2
    return 2
  fi

  printf '%s/%s\n' "$(pwd -P)" "${link##*/}"
)}

ध्यान दें कि सभी cdऔर setसामान एक उपधारा में होता है।


वास्तव में, {} के आसपास () अनावश्यक हैं, चूंकि () {} की तरह "यौगिक कमांड" के रूप में गिना जाता है। लेकिन आपको फ़ंक्शन नाम के बाद भी () की आवश्यकता है।
क्रिस कॉगडन

@ChrisCogdon फ़ंक्शन नाम के पीछे नहीं होने पर {}आसपास ()अनावश्यक नहीं हैं ()। बैश बिना फ़ंक्शन के घोषणाओं को स्वीकार करता है ()क्योंकि शेल के पास घोषणाओं में पैरामीटर सूची नहीं होती है और यह ()फ़ंक्शन फ़ंक्शन के साथ कॉल नहीं करता है, जिससे ()बहुत अधिक समझ में नहीं आता है।
solidsnack

0

यहां मैं प्रस्तुत करता हूं कि मैं जो क्रॉस-प्लेटफ़ॉर्म (लिनक्स और मैकओएस कम से कम) समाधान का जवाब देता हूं, जो वर्तमान में मेरे लिए अच्छा काम कर रहा है।

crosspath()
{
    local ref="$1"
    if [ -x "$(which realpath)" ]; then
        path="$(realpath "$ref")"
    else
        path="$(readlink -f "$ref" 2> /dev/null)"
        if [ $? -gt 0 ]; then
            if [ -x "$(which readlink)" ]; then
                if [ ! -z "$(readlink "$ref")" ]; then
                    ref="$(readlink "$ref")"
                fi
            else
                echo "realpath and readlink not available. The following may not be the final path." 1>&2
            fi
            if [ -d "$ref" ]; then
                path="$(cd "$ref"; pwd -P)"
            else
                path="$(cd $(dirname "$ref"); pwd -P)/$(basename "$ref")"
            fi
        fi
    fi
    echo "$path"
}

यहाँ एक macOS (केवल?) समाधान है। संभवतः मूल प्रश्न के अनुकूल बेहतर है।

mac_realpath()
{
    local ref="$1"
    if [[ ! -z "$(readlink "$ref")" ]]; then
        ref="$(readlink "$1")"
    fi
    if [[ -d "$ref" ]]; then
        echo "$(cd "$ref"; pwd -P)"
    else
        echo "$(cd $(dirname "$ref"); pwd -P)/$(basename "$ref")"
    fi
}

0

यहाँ मेरा जवाब है बैश: सिमिलर का असली रास्ता कैसे प्राप्त करें?

लेकिन स्क्रिप्ट में बहुत कम काम में:

script_home=$( dirname $(realpath "$0") )
echo Original script home: $script_home

ये जीएनयू कोर्यूटिल्स का हिस्सा हैं, जो लिनक्स सिस्टम में उपयोग के लिए उपयुक्त हैं।

सब कुछ का परीक्षण करने के लिए, हम सिमलिंक / होम / टेस्ट 2 / में डालते हैं, कुछ अतिरिक्त चीजों में संशोधन करते हैं और रूट डायरेक्टरी से कॉल / रन करते हैं:

/$ /home/test2/symlink
/home/test
Original script home: /home/test

कहाँ पे

Original script is: /home/test/realscript.sh
Called script is: /home/test2/symlink

0

ऐसे मामले में जहां pwd का उपयोग नहीं किया जा सकता है (जैसे किसी भिन्न स्थान से स्क्रिप्ट को कॉल करना), realpath (dnname के साथ या बिना) का उपयोग करें:

$(dirname $(realpath $PATH_TO_BE_RESOLVED))

किसी भी स्थान से - (मल्टीपल) सिम्लिंक (ओं) को कॉल करते समय या स्क्रिप्ट को सीधे कॉल करते समय दोनों काम करता है।

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