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




shell scripting (10)

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

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

$ 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 निर्देशिका से निष्पादित किया गया है, तो हल करें ("foo") == "/ उपयोगकर्ता / ग्रेग / tmp / bar"।


"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

आम शैल स्क्रिप्ट को अक्सर "होम" निर्देशिका मिलनी पड़ती है, भले ही उन्हें सिम्लिंक के रूप में बुलाया जाए। इस प्रकार लिपि को अपनी "असली" स्थिति केवल $ 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`

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

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

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

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

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/[email protected] -> ../Cellar/maven/3.2.3/bin/mvn

इस फ़ंक्शन का उपयोग करें और यह -real-path वापस कर देगा:

%% 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 

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

सी फंक्शन char *getcwd(char *buf, size_t size) एक ही व्यवहार होना चाहिए।

getcwd pwd


मेरा मानना ​​है कि यह एक सिम्लिंक को हल करने का सही और निश्चित "तरीका है" चाहे वह एक निर्देशिका या गैर-निर्देशिका है, बैश का उपयोग करके:

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 "$link"
    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 सामान सबहेल में होते हैं।


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

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

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


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

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'))")

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"))/..

readlink -e [filepath]

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





symlink