नोट: मेरा मानना है कि यह एक ठोस, पोर्टेबल, रेडी-मेड समाधान है, जो कि बहुत ही कारण से काफी लंबा है।
नीचे एक पूरी तरह से POSIX- अनुरूप स्क्रिप्ट / फ़ंक्शन है, इसलिए यह क्रॉस-प्लेटफ़ॉर्म (macOS पर भी काम करता है, जिसका readlink
अभी भी -f
10.12 (सिएरा) के रूप में समर्थन नहीं करता है ) - यह केवल POSIX शेल भाषा सुविधाओं और केवल POSIX- कंपाइलेंट उपयोगिता कॉल का उपयोग करता है ।
यह GNU'sreadlink -e
(सख्त संस्करण readlink -f
) का एक पोर्टेबल कार्यान्वयन है ।
आप कर सकते हैं चलाने स्क्रिप्ट के साथsh
या स्रोत समारोह में bash
, ksh
औरzsh
:
उदाहरण के लिए, एक स्क्रिप्ट के अंदर आप इसका उपयोग इस प्रकार कर सकते हैं कि चल रही स्क्रिप्ट की वास्तविक निर्देशिका प्राप्त करने के लिए, सहानुभूति के साथ हल:
trueScriptDir=$(dirname -- "$(rreadlink "$0")")
rreadlink
स्क्रिप्ट / फ़ंक्शन परिभाषा:
इस उत्तर से कोड को आभार के साथ अनुकूलित किया गया था ।
मैंने यहां एक bash
-आधारित स्टैंड-अलोन यूटिलिटी वर्जन भी बनाया है , जिसे आप
npm install rreadlink -g
Node.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
इसके मूल अर्थ पर भरोसा नहीं कर सकते, तब तक जो मैं बता सकता हूं, सभी दुर्भावनापूर्ण पुनर्वितरणों से बचाव का कोई गारंटी तरीका नहीं है।