ओएस एक्स के साथ बैश स्क्रिप्ट निरपेक्ष पथ


98

मैं OS X पर वर्तमान में चल रही स्क्रिप्ट के लिए पूर्ण पथ प्राप्त करने का प्रयास कर रहा हूं।

मैंने कई उत्तर देखे readlink -f $0। हालाँकि, चूंकि OS X का readlinkBSD के समान है, यह सिर्फ काम नहीं करता है (यह GNU के संस्करण के साथ काम करता है)।

क्या इसका कोई आउट-ऑफ-द-बॉक्स समाधान है?





16
$( cd "$(dirname "$0")" ; pwd -P )
जेसन एस

जवाबों:


88

एक realpath()सी फ़ंक्शन है जो काम करेगा, लेकिन मैं कमांड-लाइन पर उपलब्ध कुछ भी नहीं देख रहा हूं। यहाँ एक त्वरित और गंदा प्रतिस्थापन है:

#!/bin/bash

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

realpath "$0"

यह मार्ग शब्दशः प्रिंट करता है यदि यह एक के साथ शुरू होता है /। यदि यह एक सापेक्ष पथ नहीं होना चाहिए, तो यह $PWDसामने की ओर झुकता है। #./हिस्सा बंद स्ट्रिप्स ./के सामने से $1


1
मैंने C फ़ंक्शन पर भी ध्यान दिया लेकिन कोई भी बाइनरी या कुछ भी नहीं पा सका। वैसे भी, आपका फंक्शन सिर्फ मेरी जरूरतों को अच्छी तरह से फिट करता है। धन्यवाद!
द माइटी रबर डक

7
ध्यान दें कि यह कोई सहानुभूति नहीं है।
एडम वंडेनबर्ग

17
realpath ../somethingरिटर्न$PWD/../something
लैरी

यह मेरे लिए काम करता है, लेकिन मैंने इसका नाम बदलकर "realpath_osx ()" कर दिया है क्योंकि मुझे इस bash स्क्रिप्ट को एक linux shell पर भेजने की आवश्यकता है और यह नहीं चाहता कि यह "real" realpath से टकराए! (मुझे यकीन है कि एक और अधिक सुंदर तरीका है, लेकिन मैं एक बैश n00b हूं।)
एंड्रयू थेकेन

8
command -v realpath >/dev/null 2>&1 || realpath() { ... }
कारा ब्राइटवेल

115

यह तीन सरल कदम इसे और कई अन्य ओएस एक्स मुद्दों को हल करने जा रहे हैं:

  1. Homebrew स्थापित करें
  2. brew install coreutils
  3. grealpath .

(3) सिर्फ realpathदेखने के लिए बदला जा सकता है , (2) आउटपुट


3
यह समस्या को हल करता है, लेकिन उस सामान को स्थापित करने की आवश्यकता है जिसे आप नहीं चाहते या आवश्यकता नहीं है, या जो लक्ष्य प्रणाली पर नहीं हो सकता है, अर्थात ओएस एक्स
जेसन एस

6
@JasonS GNU Coreutils का उपयोग करने के लिए बहुत अच्छा है । इसमें बहुत सारी महान उपयोगिताएँ शामिल हैं। यह वास्तव में बहुत अच्छा है कि लिनक्स कर्नेल इसके बिना बेकार है और कुछ लोग इसे GNU / लिनक्स क्यों कहते हैं। Coreutils कमाल है। उत्कृष्ट उत्तर यह चयनित उत्तर होना चाहिए।

7
@omouse प्रश्न में विशेष रूप से 'आउट ऑफ द बॉक्स सॉल्यूशन' (OS X के लिए) का उल्लेख है। चाहे कितना भी बढ़िया कोरूटिल्स हो, यह ओएस एक्स पर 'आउट ऑफ द बॉक्स' नहीं है, इसलिए इसका जवाब अच्छा नहीं है। यह इस बात का सवाल नहीं है कि कोरुटिल कितना अच्छा है, या यह ओएस एक्स पर क्या है, उससे बेहतर है $( cd "$(dirname "$0")" ; pwd -P )। मेरे लिए 'आउट ऑफ द बॉक्स' समाधान ठीक काम करता है।
जेसन एस

2
OS X की "विशेषताओं" में से एक यह है कि निर्देशिका और फ़ाइल नाम असंवेदनशील हैं। परिणामस्वरूप यदि आपके पास कोई निर्देशिका है XXXऔर कुछ एक cd xxxतो pwdवापस आ जाएगी .../xxx। इस जवाब को छोड़कर, सभी ऊपर दिए गए समाधान xxxजब आप वास्तव में चाहते हैं, तब वापस लौटते हैं XXX। धन्यवाद!
एंड्रयू

1
मैं नहीं जानता कि कॉपी करना, चिपकाना, और बिना किसी कोड के कोड को फिर से इंस्टॉल करना मौजूदा पैकेज मैनेजर सॉल्यूशन से बेहतर है। यदि आपको जरूरत है realpath, तो क्या होता है जब आपको लगभग निश्चित रूप से अन्य वस्तुओं की आवश्यकता होगी coreutils? उन कार्यों को दफनाने में भी, फिर से लिखें? : पी
ईजेकील विक्टर

28

ओह। मुझे कुछ कारणों से पूर्व के उत्तर थोड़े चाहिए थे: विशेष रूप से, वे प्रतीकात्मक लिंक के कई स्तरों को हल नहीं करते हैं, और वे बेहद "बाश-य" हैं। जबकि मूल प्रश्न स्पष्ट रूप से "बैश स्क्रिप्ट" के लिए कहता है, यह मैक ओएस एक्स के बीएसडी-जैसे, गैर-जीएनयू का भी उल्लेख करता है readlink। तो यहाँ कुछ उचित पोर्टेबिलिटी पर प्रयास किया गया है (मैंने इसे 'श' और डैश के रूप में बैश के साथ जांचा है), प्रतीकात्मक लिंक की एक मनमानी संख्या को हल करते हुए; और इसे पथ (नों) में व्हाट्सएप के साथ भी काम करना चाहिए, हालांकि मैं व्यवहार के बारे में सुनिश्चित नहीं हूं अगर सफेद जगह उपयोगिता का आधार नाम है, तो शायद, उम, इससे बचें?

#!/bin/sh
realpath() {
  OURPWD=$PWD
  cd "$(dirname "$1")"
  LINK=$(readlink "$(basename "$1")")
  while [ "$LINK" ]; do
    cd "$(dirname "$LINK")"
    LINK=$(readlink "$(basename "$1")")
  done
  REALPATH="$PWD/$(basename "$1")"
  cd "$OURPWD"
  echo "$REALPATH"
}
realpath "$@"

आशा है कि किसी के लिए कुछ काम का हो सकता है।


1
मैं केवल localवैश्विक नाम स्थान को प्रदूषित नहीं करने के लिए फ़ंक्शन के अंदर परिभाषित चर के लिए उपयोग करने की सिफारिश करूंगा । जैसे local OURPWD=...। कम से कम बैश के लिए काम करता है।
माइकल पेसोल्ड

इसके अलावा, कोड को निजी चर के लिए अपरकेस का उपयोग नहीं करना चाहिए। सिस्टम उपयोग के लिए अपरकेस चर आरक्षित हैं।
11

स्क्रिप्ट के लिए धन्यवाद। यदि लिंक (नों) और वास्तविक फ़ाइल के अलग-अलग आधार नाम हैं, तो संभवत: यह एक अच्छा विचार होगा BASENAME=$(basename "$LINK") कि कुछ समय के अंदर जोड़ना और दूसरे लिंक सेटर और REALPATH सेटर
स्ट्रोबेरोबो

यह सिम्बलिंक को संभालता नहीं है और ..माता - पिता को काफी पसंद आता है realpath। होमब्रे के coreutilsस्थापित होने के ln -s /var/log /tmp/linkexampleबाद realpath /tmp/linkexample/../, फिर कोशिश करें ; यह प्रिंट करता है /private/var। लेकिन आपका फ़ंक्शन /tmp/linkexample/..इसके बजाय उत्पादन करता है , क्योंकि ..कोई सिमलिंक नहीं है।
मार्टिन पीटर्स

10

पायथन समाधान का अधिक कमांड-लाइन-फ्रेंडली संस्करण:

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

2
बस किसी को भी एक ही आदेश के लिए अजगर दुभाषिया शुरू करने के लिए पर्याप्त पागल है ...
बछसौ

मैं काफी पागल था, लेकिन इस्तेमाल कियाpython -c "import os; import sys; print(os.path.realpath(sys.argv[1]))"
एलेक्स चैंबरलेन

7

मैं एक प्रणाली प्रावधान स्क्रिप्ट में उपयोग के लिए एक समाधान के लिए देख रहा था, यानी, Homebrew से पहले चलाने के लिए भी स्थापित किया गया है। एक उचित समाधान खोने से मैं कार्य को एक क्रॉस-प्लेटफ़ॉर्म भाषा में लोड कर दूंगा, उदाहरण के लिए, पर्ल:

script_abspath=$(perl -e 'use Cwd "abs_path"; print abs_path(@ARGV[0])' -- "$0")

अधिक बार हम वास्तव में क्या चाहते हैं जिसमें निर्देशिका है:

here=$(perl -e 'use File::Basename; use Cwd "abs_path"; print dirname(abs_path(@ARGV[0]));' -- "$0")

महान! पर्ल छोटे उपरि के लिए अच्छा है! आप शायद पहले संस्करण को सरल बना सकते हैं FULLPATH=$(perl -e "use Cwd 'abs_path'; print abs_path('$0')")। के खिलाफ कोई कारण?
एफ परेरा

Unscaped उपयोगकर्ता आपूर्ति स्ट्रिंग से @FPereira जनरेटिंग प्रोग्राम कोड एक अच्छा विचार नहीं है। ''बुलेट प्रूफ नहीं है। $0उदाहरण के लिए, यदि यह एकल उद्धरण है तो यह टूट जाता है । एक बहुत ही सरल उदाहरण: अपने संस्करण को अपने पूर्ण पथ से आज़माएं /tmp/'/test.shऔर कॉल करें /tmp/'/test.sh
4a11e1

या और भी सरल /tmp/'.sh,।
4a11e1

6

चूंकि अन्य लोगों ने बताया है कि एक वास्तविकपथ है:

// realpath.c
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char* argv[])
{
  if (argc > 1) {
    for (int argIter = 1; argIter < argc; ++argIter) {
      char *resolved_path_buffer = NULL;
      char *result = realpath(argv[argIter], resolved_path_buffer);

      puts(result);

      if (result != NULL) {
        free(result);
      }
    }
  }

  return 0;
}

makefile:

#Makefile
OBJ = realpath.o

%.o: %.c
      $(CC) -c -o $@ $< $(CFLAGS)

realpath: $(OBJ)
      gcc -o $@ $^ $(CFLAGS)

फिर इसके साथ संकलित करें makeऔर एक सॉफ्ट लिंक में डालें:
ln -s $(pwd)/realpath /usr/local/bin/realpath


क्या हम बस कर सकते हैं gcc realpath.c -o /usr/local/bin/realpath?
अलेक्जेंडर मिल्स

1
@AlexanderMills आपको कंपाइलर को रूट के रूप में नहीं चलाना चाहिए; और यदि आप नहीं करते हैं, तो आपके पास लिखने के लिए विशेषाधिकार नहीं होंगे/usr/local/bin
ट्रिपल

6

इसे प्राप्त करने के लिए अजगर का उपयोग करें:

#!/usr/bin/env python
import os
import sys

print(os.path.realpath(sys.argv[1]))

2
abs_path () {    
   echo "$(cd $(dirname "$1");pwd)/$(basename "$1")"
}

dirnameनिर्देशिका का नाम देगा /path/to/file, अर्थात /path/to

cd /path/to; pwd यह सुनिश्चित करता है कि मार्ग निरपेक्ष है।

basenameमें सिर्फ फ़ाइल नाम दे देंगे /path/to/file, यानी file


हालांकि यह कोड प्रश्न का उत्तर दे सकता है, लेकिन यह कोड इस और क्यों या कैसे का उत्तर देता है, इस संबंध में अतिरिक्त संदर्भ प्रदान करता है।
इगोर एफ।

1

जैसा कि आप ऊपर देख सकते हैं, मैंने लगभग 6 महीने पहले इस पर एक शॉट लिया था। मैं इसके बारे में पूरी तरह से भूल गया जब तक कि मुझे खुद को फिर से इसी तरह की चीज की आवश्यकता नहीं मिली। मैं एकदम से चौंक गया कि यह कैसे अल्पविकसित था; मैं अपने आप को अब लगभग एक साल के लिए बहुत गहनता से कोड करना सिखा रहा हूं, लेकिन मुझे अक्सर लगता है कि शायद मैंने कुछ भी नहीं सीखा है जब चीजें उनके सबसे खराब हैं।

मैं ऊपर दिए गए 'समाधान' को निकाल दूंगा, लेकिन मुझे वास्तव में यह पसंद है कि पिछले कुछ महीनों में मैंने कितना कुछ सीखा है।

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

#!/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

1
जीस्ट लिंक टूटा हुआ लगता है।
एलेक्स

यह उत्तर काम करता है: stackoverflow.com/a/46772980/1223975 .... आपको इसका उपयोग करना चाहिए।
अलेक्जेंडर मिल्स

ध्यान दें कि आपका कार्यान्वयन ..माता-पिता के संदर्भ से पहले सहानुभूति का समाधान नहीं करता है ; उदाहरण के लिए सिम्लिंक जो भी पथ के जनक के बजाय के /foo/link_to_other_directory/..रूप में हल किया गया है। और रूट पर शुरू होने वाले प्रत्येक पथ घटक को हल करें और शेष लिंक लक्ष्यों को अभी भी संसाधित किए जा रहे हैं। मैंने इस तर्क को पुष्ट करते हुए इस प्रश्न का उत्तर जोड़ा है। /foo/foo/link_to_other_directoryreadlink -frealpath
मार्टिन पीटर्स

1

मैक ओएस एक्स के लिए realpath

realpath() {
    path=`eval echo "$1"`
    folder=$(dirname "$path")
    echo $(cd "$folder"; pwd)/$(basename "$path"); 
}

संबंधित पथ के साथ उदाहरण:

realpath "../scripts/test.sh"

होम फोल्डर के साथ उदाहरण

realpath "~/Test/../Test/scripts/test.sh"

1
अच्छा सरल समाधान, मुझे सिर्फ एक चेतावनी मिली, जब इसे लागू करने से ..इसका सही उत्तर नहीं मिलता है, इसलिए मैंने एक चेक जोड़ा कि यदि दिया गया मार्ग एक निर्देशिका है: if test -d $path ; then echo $(cd "$path"; pwd) ; else [...]
हर्बर्ट

मेरे लिए काम नहीं किया, जबकि "$(dirname $(dirname $(realpath $0)))"काम करता है, इसलिए कुछ और की जरूरत है ...
अलेक्जेंडर मिल्स

की बेकार उपयोगecho भी बढ़ावा नहीं दिया जाना चाहिए।
ट्रिपल

यह वास्तव में सहानुभूति को हल नहीं करता है जिस तरह से वास्तविकपथ होगा। यह सहानुभूति को हल करने से पहले.. माता-पिता के संदर्भों को हल करता है , बाद में नहीं; coreutilsस्थापित होमब्रे के साथ यह कोशिश करें , ln -s /var/log /tmp/linkexampleफिर रन के साथ लिंक बनाएं realpath /tmp/linkexample/../; यह प्रिंट करता है /private/var। लेकिन आपका फ़ंक्शन /tmp/linkexample/..इसके बजाय पैदा करता है , क्योंकि pwdअभी भी /tmp/linkexampleसीडी के बाद पता चलता है ।
मार्टिन पीटर्स

1

MacOS पर, एकमात्र समाधान जो मैंने इसको पाया है कि मज़बूती से सीमलिंक संभालता है realpath। चूंकि यह आवश्यक है brew install coreutils, मैं बस उस कदम को स्वचालित करता हूं। मेरा कार्यान्वयन इस तरह दिखता है:

#!/usr/bin/env bash

set -e

if ! which realpath >&/dev/null; then
  if ! which brew >&/dev/null; then
    msg="ERROR: This script requires brew. See https://brew.sh for installation instructions."
    echo "$(tput setaf 1)$msg$(tput sgr0)" >&2
    exit 1
  fi
  echo "Installing coreutils/realpath"
  brew install coreutils >&/dev/null
fi

thisDir=$( dirname "`realpath "$0"`" )
echo "This script is run from \"$thisDir\""


यह त्रुटियां अगर उन्होंने brewस्थापित नहीं की हैं , लेकिन आप वैकल्पिक रूप से बस भी स्थापित कर सकते हैं। मैं अभी कुछ सहज महसूस नहीं कर रहा था जो नेट से रूबी कोड को कर्ल करता है।

ध्यान दें कि ओलेग मिखेव के जवाब पर यह एक स्वचालित बदलाव है ।


एक महत्वपूर्ण परीक्षण

इनमें से किसी भी एक समाधान की एक अच्छी परीक्षा है:

  1. कोड को स्क्रिप्ट फ़ाइल में कहीं रखें
  2. किसी अन्य निर्देशिका में, ln -sउस फ़ाइल के लिए सिमलिंक ( )
  3. स्क्रिप्ट को उस सिम्लिंक से चलाएं

क्या समाधान सहिष्णुता को सीमित करता है, और आपको मूल निर्देशिका देता है? यदि हां, तो यह काम करता है।


0

यह OSX के लिए काम करता है, किसी भी बायनेरिज़ की आवश्यकता नहीं है, और यहां से खींच लिया गया था

function normpath() {
  # Remove all /./ sequences.
  local path=${1//\/.\//\/}

  # Remove dir/.. sequences.
  while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
    path=${path/${BASH_REMATCH[0]}/}
  done
  echo $path
}

0

यह मुझे पंसद है:

#!/usr/bin/env bash
function realpath() {
    local _X="$PWD"
    local _LNK=$1
    cd "$(dirname "$_LNK")"
    if [ -h "$_LNK" ]; then
        _LNK="$(readlink "$_LNK")"
        cd "$(dirname "$_LNK")"
    fi
    echo "$PWD/$(basename "$_LNK")"
    cd "$_X"
}

0

मैं एक की जरूरत realpathओएस एक्स पर प्रतिस्थापन, एक कि सिमलिंक और माता पिता के संदर्भ के साथ पथ पर सही ढंग से संचालित की तरह ही readlink -fचाहेंगे । इसमें माता-पिता के संदर्भों को हल करने से पहले मार्ग में सीलिंग को हल करना शामिल है ; उदाहरण के लिए यदि आपने होमब्रे coreutilsबोतल स्थापित की है , तो चलाएं:

$ ln -s /var/log/cups /tmp/linkeddir  # symlink to another directory
$ greadlink -f /tmp/linkeddir/..      # canonical path of the link parent
/private/var/log

ध्यान दें कि मूल dir संदर्भ readlink -fको हल करने से /tmp/linkeddir पहले हल किया गया है ..। बेशक, वहाँ कोई है readlink -fमैक पर या तो

तो realpathमैं बश 3.2 में एक GNUlib canonicalize_filename_mode(path, CAN_ALL_BUT_LAST)फ़ंक्शन कॉल क्या करता है , के लिए बैश कार्यान्वयन के भाग के रूप में फिर से लागू किया गया ; यह भी फ़ंक्शन कॉल है जिसे GNU readlink -fबनाता है:

# shellcheck shell=bash
set -euo pipefail

_contains() {
    # return true if first argument is present in the other arguments
    local elem value

    value="$1"
    shift

    for elem in "$@"; do 
        if [[ $elem == "$value" ]]; then
            return 0
        fi
    done
    return 1
}

_canonicalize_filename_mode() {
    # resolve any symlink targets, GNU readlink -f style
    # where every path component except the last should exist and is
    # resolved if it is a symlink. This is essentially a re-implementation
    # of canonicalize_filename_mode(path, CAN_ALL_BUT_LAST).
    # takes the path to canonicalize as first argument

    local path result component seen
    seen=()
    path="$1"
    result="/"
    if [[ $path != /* ]]; then  # add in current working dir if relative
        result="$PWD"
    fi
    while [[ -n $path ]]; do
        component="${path%%/*}"
        case "$component" in
            '') # empty because it started with /
                path="${path:1}" ;;
            .)  # ./ current directory, do nothing
                path="${path:1}" ;;
            ..) # ../ parent directory
                if [[ $result != "/" ]]; then  # not at the root?
                    result="${result%/*}"      # then remove one element from the path
                fi
                path="${path:2}" ;;
            *)
                # add this component to the result, remove from path
                if [[ $result != */ ]]; then
                    result="$result/"
                fi
                result="$result$component"
                path="${path:${#component}}"
                # element must exist, unless this is the final component
                if [[ $path =~ [^/] && ! -e $result ]]; then
                    echo "$1: No such file or directory" >&2
                    return 1
                fi
                # if the result is a link, prefix it to the path, to continue resolving
                if [[ -L $result ]]; then
                    if _contains "$result" "${seen[@]+"${seen[@]}"}"; then
                        # we've seen this link before, abort
                        echo "$1: Too many levels of symbolic links" >&2
                        return 1
                    fi
                    seen+=("$result")
                    path="$(readlink "$result")$path"
                    if [[ $path = /* ]]; then
                        # if the link is absolute, restart the result from /
                        result="/"
                    elif [[ $result != "/" ]]; then
                        # otherwise remove the basename of the link from the result
                        result="${result%/*}"
                    fi
                elif [[ $path =~ [^/] && ! -d $result ]]; then
                    # otherwise all but the last element must be a dir
                    echo "$1: Not a directory" >&2
                    return 1
                fi
                ;;
        esac
    done
    echo "$result"
}

इसमें सर्कुलर सिम्लिंक डिटेक्शन शामिल है, यदि एक ही (मध्यस्थ) पथ को दो बार देखा जाता है, तो बाहर निकलता है।

यदि आप सभी की जरूरत है readlink -f, तो आप उपरोक्त का उपयोग कर सकते हैं:

readlink() {
    if [[ $1 != -f ]]; then  # poor-man's option parsing
        # delegate to the standard readlink command
        command readlink "$@"
        return
    fi

    local path result seenerr
    shift
    seenerr=
    for path in "$@"; do
        # by default readlink suppresses error messages
        if ! result=$(_canonicalize_filename_mode "$path" 2>/dev/null); then
            seenerr=1
            continue
        fi
        echo "$result"
    done
    if [[ $seenerr ]]; then
        return 1;
    fi
}

इसके लिए realpath, मुझे भी जरूरत --relative-toऔर --relative-baseसमर्थन था, जो आपको कैनोनिकलाइजिंग के बाद सापेक्ष रास्ते देते हैं:

_realpath() {
    # GNU realpath replacement for bash 3.2 (OS X)
    # accepts --relative-to= and --relative-base options
    # and produces canonical (relative or absolute) paths for each
    # argument on stdout, errors on stderr, and returns 0 on success
    # and 1 if at least 1 path triggered an error.

    local relative_to relative_base seenerr path

    relative_to=
    relative_base=
    seenerr=

    while [[ $# -gt 0 ]]; do
        case $1 in
            "--relative-to="*)
                relative_to=$(_canonicalize_filename_mode "${1#*=}")
                shift 1;;
            "--relative-base="*)
                relative_base=$(_canonicalize_filename_mode "${1#*=}")
                shift 1;;
            *)
                break;;
        esac
    done

    if [[
        -n $relative_to
        && -n $relative_base
        && ${relative_to#${relative_base}/} == "$relative_to"
    ]]; then
        # relative_to is not a subdir of relative_base -> ignore both
        relative_to=
        relative_base=
    elif [[ -z $relative_to && -n $relative_base ]]; then
        # if relative_to has not been set but relative_base has, then
        # set relative_to from relative_base, simplifies logic later on
        relative_to="$relative_base"
    fi

    for path in "$@"; do
        if ! real=$(_canonicalize_filename_mode "$path"); then
            seenerr=1
            continue
        fi

        # make path relative if so required
        if [[
            -n $relative_to
            && ( # path must not be outside relative_base to be made relative
                -z $relative_base || ${real#${relative_base}/} != "$real"
            )
        ]]; then
            local common_part parentrefs

            common_part="$relative_to"
            parentrefs=
            while [[ ${real#${common_part}/} == "$real" ]]; do
                common_part="$(dirname "$common_part")"
                parentrefs="..${parentrefs:+/$parentrefs}"
            done

            if [[ $common_part != "/" ]]; then
                real="${parentrefs:+${parentrefs}/}${real#${common_part}/}"
            fi
        fi

        echo "$real"
    done
    if [[ $seenerr ]]; then
        return 1
    fi
}

if ! command -v realpath > /dev/null 2>&1; then
    # realpath is not available on OSX unless you install the `coreutils` brew
    realpath() { _realpath "$@"; }
fi

मैंने इस कोड के लिए अपने कोड समीक्षा अनुरोध में इकाई परीक्षण शामिल किए ।


-2

टिप्पणीकार के साथ संचार के आधार पर, मैं इस बात से सहमत था कि यह बहुत कठिन है और उबंटू को लागू करने के लिए कोई भी उपयुक्त तरीका नहीं है।

लेकिन निम्नलिखित संस्करण, मैकबुक पर मेरी दैनिक जरूरतों को पूरा नहीं कर सकता है और कोने के मामलों को बेहतर तरीके से संभाल सकता है। इस कोड को अपने ~ / .bashrc में रखें और याद रखें:

  • arg केवल 1 फ़ाइल या dir हो सकता है, कोई वाइल्डकार्ड नहीं
  • डीआईआर या फ़ाइल नाम में कोई स्थान नहीं
  • कम से कम फ़ाइल या डीआईआर के माता-पिता की डायर मौजूद है
  • बेझिझक उपयोग करें। .. / बात, ये सुरक्षित हैं

    # 1. if is a dir, try cd and pwd
    # 2. if is a file, try cd its parent and concat dir+file
    realpath() {
     [ "$1" = "" ] && return 1

     dir=`dirname "$1"`
     file=`basename "$1"`

     last=`pwd`

     [ -d "$dir" ] && cd $dir || return 1
     if [ -d "$file" ];
     then
       # case 1
       cd $file && pwd || return 1
     else
       # case 2
       echo `pwd`/$file | sed 's/\/\//\//g'
     fi

     cd $last
    }

आप बेकार के उपयोगecho से बचना चाहते हैं । बस शेल की दूसरी प्रति के बिना होने pwdके समान ही है echo $(pwd)। इसके अलावा, तर्क को उद्धृत नहीं echoकरना एक बग है (आप किसी भी अग्रणी या अनुगामी व्हाट्सएप को खो देंगे, किसी भी निकटवर्ती आंतरिक व्हाट्सएप वर्ण, और वाइल्डकार्ड विस्तारित, आदि)। आगे देखें stackoverflow.com/questions/10067266/…
tripleee

इसके अलावा, बिना किसी पथ के लिए व्यवहार छोटी गाड़ी है; लेकिन मुझे लगता है कि "लेकिन याद रखना" वाक्य शायद कहना चाह रहा है। हालांकि Ubuntu पर व्यवहार निश्चित रूप से वर्तमान निर्देशिका को प्रिंट करने के लिए नहीं है, जब आप realpathएक गैर-निर्देशिका निर्देशिका का अनुरोध करते हैं।

स्थिरता के लिए, संभवतः dir=$(dirname "$1"); file=$(basename "$1")लंबे-अप्रकट बैकटिक सिंटैक्स के बजाय पसंद करते हैं । साथ ही तर्कों के सही उद्धरण पर ध्यान दें।
ट्रिपल

आपका अपडेट किया गया उत्तर कई बगों को ठीक करने में विफल लगता है, और नए जोड़ता है।
ट्रिपल

कृपया मुझे कुछ विशिष्ट विफल मामले दें, क्योंकि ubuntu 18.04 डेस्कटॉप में मैं जो भी परीक्षण करता हूं वह ठीक है, धन्यवाद।
ओशिनिया
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.