कैसे संक्षिप्त करें / पथ / करने के लिए / फ़ाइल / पी / टी / फ़ाइल के लिए


9

मैं एक सुंदर एक-लाइनर (जैसे awk) की तलाश कर रहा हूं जो प्रत्येक माता-पिता / मध्यवर्ती स्तर के पहले चरित्र का उपयोग करते हुए एक यूनिक्स पथ की एक स्ट्रिंग को छोटा कर देगा, लेकिन पूर्ण आधार। उदाहरणों द्वारा दिखाना आसान:

  • /path/to/file/p/t/file
  • /tmp/tmp
  • /foo/bar/.config/wizard_magic/f/b/./wizard_magic
  • /foo/bar/.config/wizard_magic/f/b/.c/wizard_magic
    @ MichaelKjörling और @ChrisH द्वारा अच्छे बिंदुओं के प्रकाश में, यह उदाहरण दिखाता है कि कैसे हम पहले दो अक्षर दिखा सकते हैं जब पहला पात्र एक बिंदु होता है।

एक सुझाव (मैं आपके उपयोग के मामले को नहीं जानता): के बजाय संक्षिप्त /f/b/.c/wizard_magic। एक विशेष निर्देशिका में डॉट अक्सर इतना सामान्य होता है, जहां आपको दिखना चाहिए।
क्रिस एच

@ChrisH ने जो कहा उसके अलावा, .सामान्य रूप से "वर्तमान निर्देशिका" का अर्थ है। तो /f/b/./wizard_magicऐसा ही है /f/b/wizard_magicक्योंकि पथ तत्व ./एक खाली पथ तत्व को संपीड़ित करता है।
बजे एक CVn

आपको इसकी आवश्यकता क्यों है? क्या आप अपने इंटरेक्टिव शेल में कुछ चतुर
ऑटोकम्प्लीशन का

जवाबों:


7

इस परीक्षण फ़ाइल के लिए:

$ cat path
/path/to/file
/tmp
/foo/bar/.config/wizard_magic

संक्षिप्त नाम इस awk कोड के साथ उत्पन्न किए जा सकते हैं:

$ awk -F/ '{for (i=1;i<NF;i++) $i=substr($i,1,1)} 1' OFS=/ path
/p/t/file
/tmp
/f/b/./wizard_magic

Edit1: डॉट-नामों के लिए दो वर्णों का उपयोग करना

यह संस्करण निर्देशिका के नामों को एक वर्ण को छोड़कर नाम के अलावा .दो वर्णों के साथ शुरू होने वाले नामों को दर्शाता है:

$ awk -F/ '{for (i=1;i<NF;i++) $i=substr($i,1,1+($i~/^[.]/))} 1' OFS=/ path
/p/t/file
/tmp
/f/b/.c/wizard_magic

यह काम किस प्रकार करता है

  • -F/

    यह awk को इनपुट पर क्षेत्र विभाजक के रूप में स्लैश का उपयोग करने के लिए बताता है।

  • for (i=1;i<NF;i++) $i=substr($i,1,1)

    यह अंतिम को छोड़कर प्रत्येक क्षेत्र पर लूप करता है, और इसे केवल अपने पहले चरित्र से बदल देता है।

    EDIT1: संशोधित संस्करण में, हम फ़ील्ड के शुरू होने पर प्रतिस्थापन 2 की लंबाई बनाते हैं .

  • 1

    यह जागृत को संशोधित रेखा को प्रिंट करने के लिए कहता है।

  • OFS=/

    यह आउटपुट पर फ़ील्ड विभाजक के रूप में स्लैश का उपयोग करने के लिए अजीब बताता है।


उत्कृष्ट उत्तर, विभाजक का उपयोग करने के लिए मामूली संशोधन : awk -F/ '{for (i=1;i<NF;i++) $i=substr($i,1,1+($i~/^[.]/))(i==1||length($i)<2?"":"‥")} 1' OFS=/ <<<$PWDदेता है: /foo/bar/.config/wizard_magic/f‥/b‥/.c‥/wizard_magic
विचारमेन ४२ '२ 5

12

सीड में बहुत आसान है (यह मानते हुए कि फ़ाइल नामों में कोई नई सूची नहीं है):

sed 's!\([^/]\)[^/]*/!\1/!g'

Awk में कम आसान है क्योंकि इसमें बैकरेफ़रेंस (गॉक को छोड़कर, लेकिन एक अनाड़ी वाक्य रचना के साथ) का अभाव है:

awk -v FS=/ -v OFS=/ '{for (i=1; i<NF; i++) $i=substr($i,1,1)} 1'

Zsh में (पथ के साथ $full_path):

echo "${(j:/:)${(@r:1:)${(@s:/:)${full_path:h}}}}/${full_path:t}"

2
IIRC, "बैकरेफरेंस" पैटर्न में होने वाले समूहों को पकड़ने के लिए संदर्भ हैं, प्रतिस्थापन स्ट्रिंग में नहीं।
राइमॉइड

@Rhymoid \1प्रतिस्थापन स्ट्रिंग में है पैटर्न में एक कैप्चर समूह के लिए एक संदर्भ मतलब है। एक backreference एक backreference है कोई फर्क नहीं पड़ता कि आप इसका उपयोग करते हैं।
गिल्स एसओ- बुराई को रोकना 'डिक

8

आप इसे कर सकते हैं:

cd /usr///.//share/../share//man/man1 || exit
IFS=/; set -f
printf %.1s/  ${PWD%/*}
printf %s\\n "${PWD##*/}"

/u/s/m/man1

और यहाँ है sed:

printf %s "$file" |
tr /\\n \\n/      | sed -et$ \
    -e '\|^\.\.$|{x;s|\(.*\)\n.*$|\1|;x;}'  \
    -e 's|^\.\{0,2\}$||;\|.|H;$!d;x'        \
-e$ -e '\|\(\.\{0,2\}.\)\(.*\)\(\n\)|!b'    \
    -e 's||\1\3\2\3|;P;s|\n||;D' |
tr /\\n \\n/

कि सभी एक ही सामान नीचे कार्य करने के लिए करीब आता है। यह टिल्ड्स के साथ संक्षिप्त नहीं है या $PWDएक प्रमुख गैर-स्लैश के लिए सिर को सम्मिलित करता है जैसा कि फ़ंक्शन करता है (और वास्तव में, कभी भी प्रमुख स्लैश प्रिंट नहीं करता है) लेकिन बाद में इसे संभाला जा सकता है। यह अशक्त पथ घटकों, और एकल बिंदुओं, और मातम की प्रक्रिया करता है.. मामलों को है।

इसके ऊपर एक ही manरास्ता दिया गया है cd:

u/s/m/man1

यह प्रत्येक पथ घटक के लिए एक या दो अतिरिक्त अग्रणी डॉट्स भी मुद्रित करेगा जो इस तरह के साथ शुरू होता है और सिर्फ एक या दो डॉट्स नहीं है।

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

pathbytes(){
    local IFS=/   o="$-" p
    set -f${ZSH_VERSION+LFy}
    set -- ${1:-$PWD}
    for p   in      /${1:+$PWD} $*
    do      case    $p in   (.|"")  ;;
            (..)    ${1+shift}      ;;
            (/)     set --          ;;
            (*)     set -- $p $*;   esac
    done
    for p   in      //$* ""
    do      case   ${p:-/$3}        in
            ([!./]*)                ;;
            (..*)   set "..$@"      ;;
            (.*)    set ".$@"       ;;
            (//*) ! set "" $1 $1    ;;
            (~)   ! p=\~            ;;
            (~/*)   p="~/$2";set $HOME
                  ! while "${2+shift}" 2>&3
                    do   p="~/${p#??*/}"
                    done 3>/dev/null;;
            esac&&  set ""  "${p%"${p#$1?}"}/$2" "$p/$3"
    done;   printf %s\\n "${p:-$2}"
    set +f  "-${o:--}"
}

ताकि कभी भी निर्देशिका न बदले या किसी पथ घटक के अस्तित्व की पुष्टि करने की कोशिश न करे, लेकिन यह बार-बार /परिसीमन को निचोड़ता है और /./एकल-डॉट घटकों को पूरी तरह से छोड़ देता है, और /../दोहरे-डॉट घटकों को उचित रूप से संसाधित करता है।

जब $IFSकुछ गैर-व्हाट्सएप चरित्र पर सेट किया जाता है , तो दो या दो से अधिक $IFSवर्णों के अनुक्रम के परिणामस्वरूप एक या अधिक अशक्त क्षेत्र होंगे। इतने सारे लगातार स्लैश अशक्त-मूल्यवान तर्क के लिए काम करते हैं। एक प्रमुख $IFSचरित्र के लिए भी यही सच है । और इसलिए जब set -- $1विभाजन होता है, यदि परिणाम $1शून्य है, तो यह एक स्लेश के साथ शुरू हुआ, अन्यथा, ${1:+$PWD}यदि यह शून्य नहीं है, तो मैं सम्मिलित करता हूं $PWD। दूसरे शब्दों में, यदि पहला तर्क स्लैश के साथ शुरू नहीं होता है, तो यह $PWDपूर्वनिर्मित हो जाएगा । यह पथ सत्यापन के समान है

अन्यथा, पहला forलूप मार्ग के घटकों के क्रम को पुनरावृत्ति करता है, जैसे:

      1 2 3
1     2 3
2 1   3
3 2 1

... ऐसा करते समय यह किसी एक-डॉट या अशक्त घटकों की उपेक्षा करता है, और इसके लिए ..करता है ...

      1 .. 3
1     .. 3
      3
3

... दूसरा पास इस प्रभाव को उलट देता है, और ऐसा करते समय यह प्रत्येक घटक को 2-डॉट्स + चार , या 1-डॉट + चार , या चार को निचोड़ता है

इसलिए इसे अस्तित्व की परवाह किए बिना एक विहित मार्ग पर काम करना चाहिए।

मैंने दूसरे लूप में थोड़ा जोड़ा / घटाया। अब यह setअक्सर कम होता है (प्रत्येक [!./]*घटक के लिए केवल एक बार ) , और शॉर्ट-सर्किट caseपैटर्न का मूल्यांकन अधिकांश समय (उपर्युक्त पैटर्न के लिए धन्यवाद) , और इसके खिलाफ एक टेल-कॉल मैच-मूल्यांकन भी शामिल है ~। अंत में विहित पथ के सभी या एक प्रमुख भाग (पूरे घटकों पर विभाजित के रूप में)~ मिलान कर सकते हैं , मिलान बिट छीन लिया जाएगा और एक शाब्दिक ~प्रतिस्थापित किया जाएगा। ऐसा करने के लिए, मुझे संक्षिप्त रूप के साथ-साथ पथ की एक पूरी प्रतिलिपि बनाए रखनी होगी (क्योंकि संक्षिप्त पथ से मेल खाते हुए ~संभवतः बहुत उपयोगी नहीं होगा) , और इसलिए इसे इसमें रखा गया है$3 । अंतिमwhileयदि ~एक सबसेट के रूप में मिलान किया जाता है तो लूप ब्रांच को केवल चलाया जाता है $3

यदि आप इसे set -xट्रेस सक्षम के साथ चलाते हैं तो आप इसे देख सकते हैं।

$ (set -x;pathbytes ..abc/def/123///././//.././../.xzy/mno)
+ pathbytes ..abc/def/123///././//.././../.xzy/mno
+ local IFS=/ o=xsmi p
+ set -f
+ set -- ..abc def 123   . .   .. . .. .xzy mno
+ set --
+ set -- home
+ set -- mikeserv home
+ set -- ..abc mikeserv home
+ set -- def ..abc mikeserv home
+ set -- 123 def ..abc mikeserv home
+ shift
+ shift
+ set -- .xzy ..abc mikeserv home
+ set -- mno .xzy ..abc mikeserv home
+ set  mno mno
+ set . mno mno
+ set  .x/mno .xzy/mno
+ set .. .x/mno .xzy/mno
+ set  ..a/.x/mno ..abc/.xzy/mno
+ set  m/..a/.x/mno mikeserv/..abc/.xzy/mno
+ set  h/m/..a/.x/mno home/mikeserv/..abc/.xzy/mno
+ p=~/h/m/..a/.x/mno
+ set  home mikeserv
+ shift
+ p=~/m/..a/.x/mno
+ shift
+ p=~/..a/.x/mno
+
+ printf %s\n ~/..a/.x/mno
~/..a/.x/mno
+ set +f -xsmi

4
शांत, लेकिन मेरी आँखों को चोट लगी।
ग्लेन जैकमैन

1
@don_crissti - हाँ!
mikeserv

2

"गड़बड़" Zsh विषय ओह माई Zsh से एक पर्ल सिर्फ इतना है कि कि यूनिकोड समर्थन है ऐसा करने के लिए स्निपेट शामिल हैं:

perl -pe '
   BEGIN {
      binmode STDIN,  ":encoding(UTF-8)";
      binmode STDOUT, ":encoding(UTF-8)";
   }; s|^$HOME|~|g; s|/([^/.])[^/]*(?=/)|/$1|g; s|/\.([^/])[^/]*(?=/)|/.$1|g;
'

1

क्या आप संक्षिप्त नाम रखना चाहते हैं या अपनी कमांडलाइन के लिए इसका उपयोग करना चाहते हैं?
कमांडलाइन के लिए मेरे पास निम्नलिखित सुझाव हैं:
क्या आपके शेल में फ़ाइल पूरा होना आपकी मदद नहीं करता है?
कभी-कभी आप भाग्यशाली होते हैं और आपको कुछ विशेष नहीं करना पड़ता है:

# /path/to/file -> /p/t/file
ls -l /*/*/file 

# /tmp -> /tmp
cd /tmp

# /foo/bar/.config/wizard_magic -> /f/b/./wizard_magic
ls -l /*/*/*/wizard_magic -> /f/b/./wizard_magic

जब आपके पास कुछ निर्देशिकाएं हों, जिनमें आप रुचि रखते हैं, तो आप उपनाम का उपयोग कर सकते हैं:

alias cdto="cd /path/to"
alias cdtmp="cd /tmp"
alias cdcfg="cd /foo/bar/.config"
alias cddeep="cd /home/john/workdir/project1/version3/maven/x/y/z/and/more"

या आप अपने पसंदीदा dirs के लिए चर सेट कर सकते हैं

export p="/path/to"
export f="/foo/bar/.config"
ls -l $p/file
ls -l $f/wizard_magic

मुझे लगता है कि इन विकल्पों को इस तरह से .bashrc (या .profile) में परिभाषित फ़ंक्शन के साथ हल करने की तुलना में अधिक समझ में आता है

function x { 
   xxpath=""
   while [ $# -ne 0 ]; do
     xxpath+="${1}*/"
     shift
   done
   cd $(echo "${xxpath}")
}

और अपने अक्षरों के बीच रिक्त स्थान के साथ इस फ़ंक्शन x को कॉल करना:

 # cd /path/to
 x /p t

 # cd /tmp 
 x /t

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