आप बैश में एक फ़ाइल पथ को कैसे सामान्य करते हैं?


203

मैं बदलना चाहते हैं /foo/bar/..करने के लिए/foo

क्या कोई बैश कमांड है जो ऐसा करता है?


संपादित करें: मेरे व्यावहारिक मामले में, निर्देशिका मौजूद है।


4
इससे कोई फर्क पड़ता अगर /foo/barया यहाँ तक कि /fooवास्तव में मौजूद हैं, या आप केवल पथ नाम नियमों के अनुसार स्ट्रिंग परिवर्तन पहलू में रुचि रखते हैं?
चेन लेवी

5
@twalberg ... यह थोथा वंचित है ...
कैमिलो मार्टिन

2
@CamiloMartin बिल्कुल काल्पनिक नहीं है - यह वास्तव में क्या प्रश्न पूछता है - रूपांतरण /foo/bar/..करने के लिए /foo, और एक का उपयोग कर bashआदेश। यदि अन्य आवश्यकताएं हैं, जो नहीं बताई गई हैं, तो शायद वे होनी चाहिए ...
ट्वायबर्गबर्ग

14
@twalberg आप बहुत ज्यादा TDD -_- 'कर रहे हैं
Camilo Martin

जवाबों:


192

यदि आप रास्ते से फ़ाइल नाम का हिस्सा काटना चाहते हैं, तो "dirname" और "basename" आपके मित्र हैं, और "realpath" भी आसान है।

dirname /foo/bar/baz 
# /foo/bar 
basename /foo/bar/baz
# baz
dirname $( dirname  /foo/bar/baz  ) 
# /foo 
realpath ../foo
# ../foo: No such file or directory
realpath /tmp/../tmp/../tmp
# /tmp

realpath विकल्प

यदि realpathआपके शेल द्वारा समर्थित नहीं है, तो आप कोशिश कर सकते हैं

readlink -f /path/here/.. 

भी

readlink -m /path/there/../../ 

के रूप में ही काम करता है

realpath -s /path/here/../../

उस रास्ते में सामान्य होने के लिए मौजूद होने की जरूरत नहीं है।


5
आप में से जिन्हें OS X समाधान की आवश्यकता है, उनके लिए नीचे दिए गए एडम लिस के उत्तर की जाँच करें।
ट्रेंटन

stackoverflow.com/a/17744637/999943 यह दृढ़ता से संबंधित उत्तर है! मैं एक दिन में इन दोनों क्यूए पोस्ट पर आया था, और मैं उन्हें एक साथ जोड़ना चाहता था।
phyatt

realpath को 2012 में कोरयूटिल्स में जोड़ा गया प्रतीत होता है। फ़ाइल इतिहास github.com/coreutils/coreutils/commits/master/src/realpath.c पर देखें ।
नूह लवाइन

1
दोनों realpathऔर readlinkGNU कोर उपयोगिताओं से आता है, इसलिए शायद आप दोनों या कोई भी नहीं मिलता है। अगर मुझे ठीक से याद है, तो मैक पर पढ़ा जाने वाला संस्करण GNU एक से थोड़ा अलग है: - \
एंटोनी 'हैशर' मूसो

100

मुझे नहीं पता कि ऐसा करने के लिए एक सीधा बैश कमांड है, लेकिन मैं आमतौर पर करता हूं

normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"

और यह अच्छी तरह से काम करता है।


9
यह सामान्य हो जाएगा लेकिन नरम लिंक को हल नहीं करेगा। यह या तो बग या फीचर हो सकता है। :-)
एडम लिस

4
यह भी एक समस्या है अगर $ CDPATH परिभाषित किया गया है; क्योंकि "सीडी फू" किसी भी "फू" निर्देशिका में बदल जाएगा जो कि $ CDPATH का उपनिर्देशिका है, न कि केवल एक "फू" जो कि वर्तमान निर्देशिका में है। मुझे लगता है कि आपको कुछ करने की आवश्यकता है: CDPATH = "" cd "$ {dirToNormalize}" && pwd -P।
एमजे

7
टिम का जवाब निश्चित रूप से सबसे सरल और सबसे पोर्टेबल है। CDPATH से निपटना आसान है: dir = "$ (unset CDPATH && cd" $ dir "&& pwd)"
David Blevins

2
यह बहुत खतरनाक हो सकता है ( rm -rf $normalDir) यदि dirToNormalize मौजूद नहीं है!
फ्रैंक मेउलेनर

4
हाँ, यह संभवतः &&@ DavidBlevins की टिप्पणी के अनुसार उपयोग करना सबसे अच्छा है ।
इलायस डॉर्नेल्स 16

54

कोशिश करो realpath। नीचे इसकी संपूर्णता का स्रोत है, इसके द्वारा सार्वजनिक डोमेन को दान कर दिया गया है।

// realpath.c: display the absolute path to a file or directory.
// Adam Liss, August, 2007
// This program is provided "as-is" to the public domain, without express or
// implied warranty, for any non-profit use, provided this notice is maintained.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>   
#include <limits.h>

static char *s_pMyName;
void usage(void);

int main(int argc, char *argv[])
{
    char
        sPath[PATH_MAX];


    s_pMyName = strdup(basename(argv[0]));

    if (argc < 2)
        usage();

    printf("%s\n", realpath(argv[1], sPath));
    return 0;
}    

void usage(void)
{
    fprintf(stderr, "usage: %s PATH\n", s_pMyName);
    exit(1);
}

1
क्या यह मानक बैश स्थापित के भाग के रूप में शामिल है? मैं अपने सिस्टम पर "कमांड नहीं मिला" (आरएचईएल पर 3.00.15 (1))
टिम व्हिटकोम्ब

4
gnu.org/software/coreutils : readlink के लिए, लेकिन realpath इस पैकेज से आता है packages.debian.org/unstable/utils/realpath
केंट फ्रेडरिक

8
यह ubuntu / BSD पर मानक है, न कि Centos / OSX
Erik Aronesty

4
आपको वास्तव में "बोनस: इसका बैश कमांड, और हर जगह उपलब्ध है!" के बारे में realpath। यह मेरे पसंदीदा होने के लिए भी होता है, लेकिन स्रोत से अपने उपकरण को संकलित करने के बजाय। यह सभी हेवीवेट के लिए है, और अधिकांश समय आप बस चाहते हैं readlink -f... बीटीडब्लू, readlinkएक बैश बिलिन नहीं है, लेकिन coreutilsउबंटू का हिस्सा है ।
टॉमस गंडोर

4
आपने कहा है कि आप इसे सार्वजनिक डोमेन में दान कर रहे हैं, लेकिन हैडर "किसी गैर-लाभकारी उपयोग के लिए" भी कहता है - क्या आप वास्तव में इसे सार्वजनिक डोमेन में दान करने के लिए कहते हैं? इसका मतलब यह होगा कि इसका इस्तेमाल व्यावसायिक लाभ के उद्देश्यों के साथ-साथ गैर-लाभ के लिए भी किया जा सकता है ...
me_and

36

एक पोर्टेबल और विश्वसनीय समाधान अजगर का उपयोग करना है, जो कि हर जगह (डार्विन सहित) बहुत पहले से स्थापित है। आपके पास दो विकल्प हैं:

  1. abspath एक पूर्ण मार्ग देता है, लेकिन सहानुभूति का समाधान नहीं करता है:

    python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file

  2. realpath एक निरपेक्ष मार्ग देता है और ऐसा करने में सहानुभूति का समाधान करता है, एक विहित मार्ग उत्पन्न करता है:

    python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file

प्रत्येक मामले में, path/to/fileएक रिश्तेदार या निरपेक्ष मार्ग हो सकता है।


7
धन्यवाद, यह एकमात्र ऐसा काम था। ओएस एक्स के तहत रीडिंक या रियलपाथ उपलब्ध नहीं हैं। पायथन अधिकांश प्लेटफार्मों पर होना चाहिए।
सोरिन

1
बस स्पष्ट करने के लिए, readlinkओएस एक्स पर उपलब्ध है, बस -fविकल्प के साथ नहीं । पोर्टेबल वर्कअराउंड पर यहां चर्चा की गई ।
StvanW

3
यह सचमुच मेरे दिमाग को चकरा देता है कि यदि आप लिंक का पालन नहीं करना चाहते हैं तो यह एकमात्र एकमात्र समाधान है। यूनिक्स रास्ता मेरे पैर।
दानोहहंग

34

Coreutils पैकेज से रीडलिंक उपयोगिता का उपयोग करें।

MY_PATH=$(readlink -f "$0")

1
बीएसडी में -f ध्वज नहीं है, जिसका अर्थ है कि यह नवीनतम MacOS Mojave और कई अन्य प्रणालियों पर भी विफल हो जाएगा। यदि आप पोर्टेबिलिटी चाहते हैं -f का उपयोग करना भूल जाते हैं, तो बहुत सारे ओएस-एस प्रभावित होते हैं।
सोरिन

1
@sorin। सवाल मैक के बारे में नहीं है, यह लिनक्स के बारे में है।
मटैलिकंड्र

14

readlinkनिरपेक्ष पथ प्राप्त करने के लिए बैश मानक है। यदि पथ या पथ मौजूद नहीं है (तो ऐसा करने के लिए झंडे दिए गए हैं) यह खाली तारों को वापस करने का लाभ है।

किसी निर्देशिका के पूर्ण पथ को प्राप्त करने के लिए, जिसका अस्तित्व है या नहीं हो सकता है, लेकिन उसके माता-पिता कौन हैं, इसका उपयोग करें:

abspath=$(readlink -f $path)

सभी माता-पिता के साथ एक निर्देशिका के लिए पूर्ण पथ प्राप्त करने के लिए:

abspath=$(readlink -e $path)

दिए गए पथ को कैनोनिज़लाइज़ करने के लिए और अगर वे मौजूद हैं, तो सहानुभूति का पालन करें, लेकिन अन्यथा लापता निर्देशिकाओं को अनदेखा करें और पथ को वापस लौटा दें:

abspath=$(readlink -m $path)

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

abspath=$(cd ${path%/*} && echo $PWD/${path##*/})

यह $ पथ के निर्देशिका भाग को chdir करेगा और $ पथ के फ़ाइल भाग के साथ वर्तमान निर्देशिका मुद्रित करेगा। यदि यह chdir में विफल रहता है, तो आपको खाली स्ट्रिंग और stderr पर एक त्रुटि मिलती है।


7
readlinkयदि यह उपलब्ध है तो एक अच्छा विकल्प है। OS X संस्करण -eया -fविकल्पों का समर्थन नहीं करता है । पहले तीन उदाहरणों में, $pathफ़ाइल नाम में रिक्त स्थान या वाइल्डकार्ड को संभालने के लिए आपके पास दोहरे उद्धरण होने चाहिए । पैरामीटर विस्तार के लिए +1, लेकिन इसमें सुरक्षा भेद्यता है। यदि pathखाली है, तो यह cdआपके घर निर्देशिका के लिए होगा। आपको दोहरे उद्धरण चिह्नों की आवश्यकता है। abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
टॉक्सालॉट

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

6
यहां किसी भी अपराध का अर्थ नहीं है, लेकिन इस तरह के अस्पष्ट मुद्दों को इंगित करना महत्वपूर्ण है। मैं बस इस तुच्छ समस्या का त्वरित उत्तर चाहता हूं और मुझे उस कोड पर किसी भी / सभी इनपुट के साथ भरोसा होगा यदि यह ऊपर टिप्पणी के लिए नहीं था। इन बैश चर्चाओं में OS-X का समर्थन करना भी महत्वपूर्ण है। बहुत सारे आदेश हैं जो दुर्भाग्य से ओएस-एक्स पर समर्थित नहीं हैं, और कई फ़ॉर्म्स हैं जो बश की चर्चा करते समय दिए जाते हैं, जिसका अर्थ है कि हम बहुत सारे क्रॉस-प्लेटफ़ॉर्म मुद्दों को प्राप्त करना जारी रखेंगे जब तक कि बाद में इसके बजाय जल्दी से निपटा नहीं जाता है।
मई'14

13

पुराने प्रश्न, लेकिन अगर आप पूर्ण पथ नामों के साथ काम कर रहे हैं तो बहुत सरल तरीका है शेल स्तर पर है:

   abspath = "$ (cd" $ पथ "&& pwd)"

जैसा कि cd एक सबशेल में होता है यह मुख्य स्क्रिप्ट को प्रभावित नहीं करता है।

आपके शेल निर्मित अंतर्निहित कमांड को स्वीकार करते हुए दो भिन्नताएं -L और -P, हैं:

   abspath = "$ (cd -P" $ पाथ "&& pwd -P)" # सममित पथ जो हल किए गए सिम्बलिंक के साथ
   abspath = "$ (cd -L" $ पथ "&& pwd -L)" # धार्मिक पथ सहानुभूति को संरक्षित करना

व्यक्तिगत रूप से, मुझे शायद ही कभी इस दृष्टिकोण की आवश्यकता होती है जब तक कि मैं किसी कारण से प्रतीकात्मक लिंक के साथ मोहित नहीं होता हूं।

FYI करें: एक स्क्रिप्ट की शुरुआती निर्देशिका प्राप्त करने पर भिन्नता जो स्क्रिप्ट के बाद की वर्तमान निर्देशिका को बदलने पर भी काम करती है।

name0 = "$ (बेसन" $ 0 ")"; # स्क्रिप्ट का नाम
dir0 = "$ (cd" $ (dirname "$ 0") "&& pwd)"; # ग्लब्स को शुरू करना

सीडी का उपयोग आपको हमेशा पूर्ण निर्देशिका का आश्वासन देता है, भले ही स्क्रिप्ट को कमांड्स द्वारा चलाया जाता हो जैसे कि / .स्क्रिप्ट। S जो, बिना cd / pwd के, अक्सर बस देता है .. बेकार अगर स्क्रिप्ट बाद में सीडी बनाता है।


8

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

get_abs_path() {
     local PARENT_DIR=$(dirname "$1")
     cd "$PARENT_DIR"
     local ABS_PATH="$(pwd)"/"$(basename "$1")"
     cd - >/dev/null
     echo "$ABS_PATH"
} 

यदि आप चाहते हैं कि यह सीमलिंक को हल करे, तो इसके pwdसाथ बदलें pwd -P


pwd -Pइस मामले के विकल्प के साथ एक गेटा ... विचार करें कि क्या होगा यदि $(basename "$1")किसी अन्य निर्देशिका में एक फ़ाइल के लिए सिम्लिंक किया गया था। pwd -Pकेवल पथ की सूची हिस्से में सिमलिंक का समाधान करता है, लेकिन नहीं basename भाग।
टॉक्सालॉट

7

मेरा हालिया समाधान था:

pushd foo/bar/..
dir=`pwd`
popd

टिम व्हिटकॉम के उत्तर के आधार पर।


मुझे संदेह है कि अगर तर्क निर्देशिका नहीं है तो यह विफल हो जाता है। मान लीजिए मैं जानना चाहता / चाहती हूं कि usr / bin / java किस ओर जाता है?
एडवर्ड फॉक

1
यदि आप जानते हैं कि यह एक फ़ाइल है, तो आप pushd $(dirname /usr/bin/java)एक कोशिश दे सकते हैं ।
schmunk

5

ठीक उत्तर नहीं है लेकिन शायद एक अनुवर्ती प्रश्न (मूल प्रश्न स्पष्ट नहीं था):

readlinkयदि आप वास्तव में सहानुभूति का पालन करना चाहते हैं तो ठीक है। लेकिन वहाँ भी केवल सामान्य के लिए एक उपयोग है ./और ../और //दृश्यों, जो विशुद्ध रूप से वाक्य रचना किया जा सकता है, बिना सिमलिंक canonicalizing। readlinkइसके लिए कोई अच्छा नहीं है, और न ही है realpath

for f in $paths; do (cd $f; pwd); done

मौजूदा रास्तों के लिए काम करता है, लेकिन दूसरों के लिए टूट जाता है।

एक sedस्क्रिप्ट एक अच्छी शर्त लगती है, सिवाय इसके कि आप पर्ल की तरह कुछ का उपयोग किए बिना अनुक्रमों ( /foo/bar/baz/../..-> /foo/bar/..-> /foo) को पुनरावृत्त नहीं कर सकते हैं , जो सभी प्रणालियों पर ग्रहण करने के लिए सुरक्षित नहीं है, या कुछ बदसूरत लूप का उपयोग करने के लिए सुरक्षित नहीं हैsed है। इसका इनपुट

FWIW, जावा (JDK 6+) का उपयोग कर एक-लाइनर:

jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths

realpathएक है -sकरने के लिए सांकेतिक लिंक और के लिए एक ही संकल्प संदर्भ का समाधान नहीं विकल्प /./, /../और अतिरिक्त हटाने /अक्षर। -mविकल्प के साथ संयुक्त होने पर , realpathकेवल फ़ाइल नाम पर काम करता है, और किसी भी वास्तविक फ़ाइल को नहीं छूता है। यह सही समाधान की तरह लगता है । लेकिन अफसोस, realpathकई प्रणालियों में अभी भी गायब है।
टॉक्सालॉट

..जब सिमलिंक शामिल होते हैं, तो घटकों को निकालना कृत्रिम रूप से नहीं किया जा सकता है। /one/two/../threeके समान नहीं है /one/three, तो twoकरने के लिए एक सिमलिंक है /foo/bar
jrw32982

@ jrw32982 हाँ के रूप में मैं अपने जवाब में कहा था कि यह जब सिमलिंक कैनॉनिकलाइज़ेशन है एक उपयोग के मामले के लिए है नहीं करना चाहता था या की जरूरत है।
जेसी ग्लेक

@JesseGlick यह सिम्बलिंक को कैनोनिकलाइज़ करने के लिए है या नहीं, यह सिर्फ एक मामला नहीं है। आपका एल्गोरिथ्म वास्तव में गलत उत्तर का उत्पादन करता है। आपके उत्तर के सही होने के लिए, आपको एक प्राथमिकता को जानना होगा कि इसमें कोई सहानुभूति शामिल नहीं थी (या कि वे केवल एक निश्चित फॉर्म के थे)। आपके उत्तर में कहा गया है कि आप उन्हें विहित नहीं करना चाहते हैं, यह नहीं कि मार्ग में कोई सहानुभूति शामिल नहीं है।
jrw32982

ऐसे मामलों का उपयोग किया जाता है जहां किसी भी निश्चित, मौजूदा निर्देशिका संरचना को संभालने के बिना सामान्यीकरण किया जाना चाहिए। यूआरआई सामान्यीकरण समान है। इन मामलों में यह एक अंतर्निहित सीमा है कि परिणाम आम तौर पर सही नहीं होगा यदि एक निर्देशिका के पास सहानुभूति होती है जहां परिणाम बाद में लागू होता है।
जेसी ग्लिक

5

मुझे पार्टी के लिए देर हो रही है, लेकिन इस तरह से धागे का एक गुच्छा पढ़ने के बाद मैंने इसे तैयार किया है।

resolve_dir() {
        (builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi)
}

यह $ 1 के निरपेक्ष पथ को हल करेगा, ~ के साथ अच्छा खेलें, जिस रास्ते में वे हैं, उस जगह पर सहानुभूति रखें और यह आपकी निर्देशिका स्टैक के साथ गड़बड़ नहीं करेगा। यह पूर्ण पथ या कुछ भी नहीं देता है अगर यह मौजूद नहीं है। यह $ 1 की एक निर्देशिका होने की उम्मीद करता है और यदि यह नहीं है तो संभवतः विफल हो जाएगा, लेकिन यह खुद को करने के लिए एक आसान जांच है।


4

बातूनी, और थोड़ा देर से जवाब। जब से मैं पुराने RHEL4 / 5 पर अटका हुआ हूं, मुझे एक लिखना होगा। मैं पूर्ण और सापेक्ष लिंक संभालता हूं, और //, /./ और somedir /../ प्रविष्टियों को सरल करता हूं।

test -x /usr/bin/readlink || readlink () {
        echo $(/bin/ls -l $1 | /bin/cut -d'>' -f 2)
    }


test -x /usr/bin/realpath || realpath () {
    local PATH=/bin:/usr/bin
    local inputpath=$1
    local changemade=1
    while [ $changemade -ne 0 ]
    do
        changemade=0
        local realpath=""
        local token=
        for token in ${inputpath//\// }
        do 
            case $token in
            ""|".") # noop
                ;;
            "..") # up one directory
                changemade=1
                realpath=$(dirname $realpath)
                ;;
            *)
                if [ -h $realpath/$token ] 
                then
                    changemade=1
                    target=`readlink $realpath/$token`
                    if [ "${target:0:1}" = '/' ]
                    then
                        realpath=$target
                    else
                        realpath="$realpath/$target"
                    fi
                else
                    realpath="$realpath/$token"
                fi
                ;;
            esac
        done
        inputpath=$realpath
    done
    echo $realpath
}

mkdir -p /tmp/bar
(cd /tmp ; ln -s /tmp/bar foo; ln -s ../.././usr /tmp/bar/link2usr)
echo `realpath /tmp/foo`

3

हमारे नए बैश लाइब्रेरी उत्पाद को रियलपैथ-लिब की कोशिश करें जिसे हमने मुफ्त और बिना लाइसेंस के उपयोग के लिए GitHub में रखा है। यह पूरी तरह से प्रलेखित है और एक महान शिक्षण उपकरण बनाता है।

यह स्थानीय, रिश्तेदार और निरपेक्ष पथों को हल करता है और बैश 4+ को छोड़कर किसी पर निर्भरता नहीं है; तो यह बस के बारे में कहीं भी काम करना चाहिए। यह मुफ़्त, स्वच्छ, सरल और शिक्षाप्रद है।

तुम कर सकते हो:

get_realpath <absolute|relative|symlink|local file path>

यह समारोह पुस्तकालय का मूल है:

function get_realpath() {

if [[ -f "$1" ]]
then 
    # file *must* exist
    if cd "$(echo "${1%/*}")" &>/dev/null
    then 
        # file *may* not be local
        # exception is ./file.ext
        # try 'cd .; cd -;' *works!*
        local tmppwd="$PWD"
        cd - &>/dev/null
    else 
        # file *must* be local
        local tmppwd="$PWD"
    fi
else 
    # file *cannot* exist
    return 1 # failure
fi

# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success

}

इसमें get_dirname, get_filename, get_ stemname और validate_path के फ़ंक्शंस शामिल हैं। प्लेटफार्मों पर इसे आज़माएं, और इसे बेहतर बनाने में मदद करें।


2

इसके साथ समस्या realpathयह है कि यह बीएसडी (या उस मामले के लिए OSX) पर उपलब्ध नहीं है। यहाँ लिनक्स जर्नल से एक पुराने (2009) लेख से निकाला गया एक सरल नुस्खा है , जो काफी पोर्टेबल है:

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

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

ध्यान दें कि इस संस्करण को भी मौजूद होने के लिए पथ की आवश्यकता नहीं है।


हालाँकि यह सिमिलिंक को हल नहीं करता है। Realpath रूट पर शुरू होने वाले रास्तों को प्रोसेस करता है और जैसे-जैसे आगे बढ़ता है सिम्बलिंक्स का अनुसरण करता है। यह सब कुछ माता-पिता के संदर्भ को ध्वस्त करता है।
मार्टिन पीटर्स

2

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

यह बैश संस्करणों 3.2.25 और उच्चतर पर काम करता है।

shopt -s extglob

normalise_path() {
    local path="$1"
    # get rid of /../ example: /one/../two to /two
    path="${path//\/*([!\/])\/\.\./}"
    # get rid of /./ and //* example: /one/.///two to /one/two
    path="${path//@(\/\.\/|\/+(\/))//}"
    # remove the last '/.'
    echo "${path%%/.}"
}

$ normalise_path /home/codemedic/../codemedic////.config
/home/codemedic/.config

यह एक अच्छा विचार है, लेकिन मैंने 20 मिनट बर्बाद करने के लिए विभिन्न बैश के विभिन्न संस्करणों पर काम करने की कोशिश की। यह पता चलता है कि extglob शेल विकल्प को काम करने के लिए इस पर होना चाहिए, और यह डिफ़ॉल्ट रूप से नहीं है। जब कार्यक्षमता को कोसने की बात आती है, तो आवश्यक संस्करण और गैर-डिफ़ॉल्ट दोनों विकल्पों को निर्दिष्ट करना महत्वपूर्ण है क्योंकि ये विवरण ओएस के बीच भिन्न हो सकते हैं। उदाहरण के लिए मैक OSX (Yosemite) का हालिया संस्करण केवल बैश (3.2) के पुराने संस्करण के साथ आता है।
drwatsoncode

क्षमा करें @ricovox; मैंने अब उन्हें अपडेट किया है। मैं आपके पास वहां मौजूद बैश के सटीक संस्करण को जानने के लिए उत्सुक हूं। उपरोक्त सूत्र (अपडेटेड) CentOS 5.8 पर काम करता है जो कि बैश 3.2.25 के साथ आता है
οδεMεδι15

गलतफहमी के लिए खेद है। एक बार जब मैंने एक्सग्लोब चालू किया था, तो यह कोड मैक ओएसएक्स बैश (3.2.57) के मेरे संस्करण पर काम करता है। बैश संस्करणों के बारे में मेरा ध्यान एक अधिक सामान्य था (जो वास्तव में बैश में रेगेक्स के बारे में यहां एक और उत्तर पर लागू होता है)।
drwatsoncode

2
मैं हालांकि आपके जवाब की सराहना करता हूं। मैंने इसे अपने लिए आधार के रूप में इस्तेमाल किया। संयोग से मैं कई मामलों देखा जहां तुम्हारा विफल रहता है: (1) संबंधित पथ hello/../world(2) डॉट्स में फ़ाइल नाम /hello/..world(3) डॉट्स के बाद डबल स्लैश /hello//../world(4) डॉट से पहले या डबल स्लैश के बाद /hello//./worldया /hello/.//world वर्तमान के बाद (5) जनक: /hello/./../world/ (6) जनक माता-पिता के बाद: /hello/../../worldआदि - इनमें से कुछ को सुधार करने के लिए एक लूप का उपयोग करके तय किया जा सकता है जब तक कि मार्ग बदलना बंद न हो जाए। (इसके अलावा dir/../, नहीं, /dir/..लेकिन dir/..अंत से हटा दें ।)
drwatsoncode

0

लवबॉर्ग के उत्कृष्ट अजगर स्निपेट के आधार पर, मैंने यह लिखा:

#!/bin/sh

# Version of readlink that follows links to the end; good for Mac OS X

for file in "$@"; do
  while [ -h "$file" ]; do
    l=`readlink $file`
    case "$l" in
      /*) file="$l";;
      *) file=`dirname "$file"`/"$l"
    esac
  done
  #echo $file
  python -c "import os,sys; print os.path.abspath(sys.argv[1])" "$file"
done

0
FILEPATH="file.txt"
echo $(realpath $(dirname $FILEPATH))/$(basename $FILEPATH)

यदि फ़ाइल मौजूद नहीं है तो भी यह काम करता है। यह निर्देशिका की आवश्यकता है जिसमें फ़ाइल मौजूद है।


GNU Realpath को पथ के अंतिम तत्व की आवश्यकता नहीं है, जब तक आप उपयोग नहीं करते हैंrealpath -e
Martijn Pieters

0

मुझे एक ऐसे समाधान की आवश्यकता थी जो तीनों करे:

  • एक स्टॉक मैक पर काम करें। realpathतथाreadlink -f एडन हैं
  • सिमिलिंक का समाधान करें
  • त्रुटि से निपटने

दोनों में से कोई भी उत्तर # 1 और # 2 नहीं था। मैंने अन्य 3 याक-शेविंग को बचाने के लिए # 3 जोड़ा।

#!/bin/bash

P="${1?Specify a file path}"

[ -e "$P" ] || { echo "File does not exist: $P"; exit 1; }

while [ -h "$P" ] ; do
    ls="$(ls -ld "$P")"
    link="$(expr "$ls" : '.*-> \(.*\)$')"
    expr "$link" : '/.*' > /dev/null &&
        P="$link" ||
        P="$(dirname "$P")/$link"
done
echo "$(cd "$(dirname "$P")"; pwd)/$(basename "$P")"

यहां उद्धृत करने के लिए पूरी तरह से अभ्यास करने के लिए रास्तों में कुछ मुड़ स्थानों के साथ एक छोटा परीक्षण मामला है

mkdir -p "/tmp/test/ first path "
mkdir -p "/tmp/test/ second path "
echo "hello" > "/tmp/test/ first path / red .txt "
ln -s "/tmp/test/ first path / red .txt " "/tmp/test/ second path / green .txt "

cd  "/tmp/test/ second path "
fullpath " green .txt "
cat " green .txt "

0

मुझे पता है कि यह एक प्राचीन प्रश्न है। मैं अभी भी एक विकल्प दे रहा हूं। हाल ही में मैं एक ही मुद्दे से मिला और ऐसा करने के लिए कोई मौजूदा और पोर्टेबल कमांड नहीं मिला। इसलिए मैंने निम्नलिखित शेल स्क्रिप्ट लिखी जिसमें एक फ़ंक्शन शामिल है जो चाल कर सकता है।

#! /bin/sh                                                                                                                                                

function normalize {
  local rc=0
  local ret

  if [ $# -gt 0 ] ; then
    # invalid
    if [ "x`echo $1 | grep -E '^/\.\.'`" != "x" ] ; then
      echo $1
      return -1
    fi

    # convert to absolute path
    if [ "x`echo $1 | grep -E '^\/'`" == "x" ] ; then
      normalize "`pwd`/$1"
      return $?
    fi

    ret=`echo $1 | sed 's;/\.\($\|/\);/;g' | sed 's;/[^/]*[^/.]\+[^/]*/\.\.\($\|/\);/;g'`
  else
    read line
    normalize "$line"
    return $?
  fi

  if [ "x`echo $ret | grep -E '/\.\.?(/|$)'`" != "x" ] ; then
    ret=`normalize "$ret"`
    rc=$?
  fi

  echo "$ret"
  return $rc
}

https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c


0

मैंने उच्चतम संभव प्रदर्शन (मनोरंजन के लिए) पर ध्यान केंद्रित करने के लिए इसे बनाने के लिए एक बिल्ट-ओनली फंक्शन बनाया। यह सीमलिंक को हल नहीं करता है, इसलिए यह मूल रूप से जैसा है realpath -sm

## A bash-only mimic of `realpath -sm`. 
## Give it path[s] as argument[s] and it will convert them to clean absolute paths
abspath () { 
  ${*+false} && { >&2 echo $FUNCNAME: missing operand; return 1; };
  local c s p IFS='/';  ## path chunk, absolute path, input path, IFS for splitting paths into chunks
  local -i r=0;         ## return value

  for p in "$@"; do
    case "$p" in        ## Check for leading backslashes, identify relative/absolute path
    '') ((r|=1)); continue;;
    //[!/]*)  >&2 echo "paths =~ ^//[^/]* are impl-defined; not my problem"; ((r|=2)); continue;;
    /*) ;;
    *)  p="$PWD/$p";;   ## Prepend the current directory to form an absolute path
    esac

    s='';
    for c in $p; do     ## Let IFS split the path at '/'s
      case $c in        ### NOTE: IFS is '/'; so no quotes needed here
      ''|.) ;;          ## Skip duplicate '/'s and '/./'s
      ..) s="${s%/*}";; ## Trim the previous addition to the absolute path string
      *)  s+=/$c;;      ### NOTE: No quotes here intentionally. They make no difference, it seems
      esac;
    done;

    echo "${s:-/}";     ## If xpg_echo is set, use `echo -E` or `printf $'%s\n'` instead
  done
  return $r;
}

नोट: यह फ़ंक्शन शुरू होने वाले रास्तों को संभालता नहीं है //, क्योंकि पथ के शुरू में दो डबल स्लैश कार्यान्वयन-परिभाषित व्यवहार हैं। हालाँकि, यह ठीक है /, ///और इतना ही।

यह फ़ंक्शन सभी किनारे के मामलों को ठीक से संभालने के लिए लगता है, लेकिन वहाँ अभी भी कुछ हो सकता है जो मैंने निपटा नहीं है।

प्रदर्शन नोट: जब हजारों तर्कों के साथ बुलाया जाता है, तो abspathलगभग 10x धीमी गति से चलता है realpath -sm; जब एक ही तर्क के साथ कहा जाता है,abspath तो realpath -smमेरी मशीन पर 110x से अधिक तेजी से चलता है , ज्यादातर हर बार एक नए कार्यक्रम को निष्पादित करने की आवश्यकता नहीं होने के कारण।


-1

मुझे आज पता चला कि आप इसका उपयोग कर सकते हैं stat पथों को हल करने के कमांड का ।

तो "~ / दस्तावेज़" जैसी निर्देशिका के लिए:

आप इसे चला सकते हैं:

stat -f %N ~/Documents

पूरा रास्ता पाने के लिए:

/Users/me/Documents

सीमलिंक के लिए, आप% Y प्रारूप विकल्प का उपयोग कर सकते हैं:

stat -f %Y example_symlink

जिसका परिणाम हो सकता है जैसे:

/usr/local/sbin/example_symlink

स्वरूपण विकल्प * NIX के अन्य संस्करणों पर भिन्न हो सकते हैं लेकिन ये OSX पर मेरे लिए काम करते हैं।


1
stat -f %N ~/Documentsलाइन एक रेड हेरिंग ... अपने खोल की जगह है ~/Documentsके साथ /Users/me/Documents, और statबस शब्दशः अपने तर्क प्रिंट कर रहा है।
दनव्यानंद

-4

एक सरल समाधान का उपयोग कर node.js:

#!/usr/bin/env node
process.stdout.write(require('path').resolve(process.argv[2]));
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.