/ Path / to / file / p / t / file kısaltması nasıl yapılır


9

awkHer bir üst / orta seviyenin ilk karakterini, ancak tam basename kullanarak bir Unix yolunun bir dizesini kısaltacak zarif bir astar (örneğin ) arıyorum . Örneklerle gösterilmesi daha kolay:

  • /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
    Aşağıdaki @ MichaelKjörling ve @ChrisH tarafından verilen iyi noktalar ışığında, bu örnek ilk karakter bir nokta olduğunda ilk iki karakteri nasıl gösterebileceğimizi gösterir.

Bir öneri (kullanım durumunuzu bilmiyorum): yerine kısaltın /f/b/.c/wizard_magic. Nokta, belirli bir dizinde genellikle aradığınız yere çok küçük bir ipucu olacak kadar yaygındır.
Chris H

@ChrisH'nin söylediklerinin yanı sıra, .normalde "geçerli dizin" anlamına gelir. Yani /f/b/./wizard_magicaynıdır /f/b/wizard_magicyol elemanı çünkü ./boş bir yol elemana kompresler.
CVn

Neden buna ihtiyacın var? Eğer etkileşimli kabukta bazı akıllı otomatik tamamlama kullanılamaz (belki yeterli bir şey için kabuk değiştirme)
Basile Starynkevitch

Yanıtlar:


7

Bu test dosyası için:

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

Kısaltmalar bu awk kodu ile oluşturulabilir:

$ 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: Nokta isimleri için iki karakter kullanma

Bu sürüm, iki adla kısaltılmış adlar dışında dizin adlarını bir karakter .olarak kısaltır:

$ 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

Nasıl çalışır

  • -F/

    Bu awk'ye girişte alan ayırıcısı olarak eğik çizgi kullanmasını söyler.

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

    Bu, sonuncusu hariç her alanın üzerinde dolaşır ve onu yalnızca ilk karakteriyle değiştirir.

    DÜZENLEME1: Gözden geçirilmiş sürümde, alan başladığında alt dizenin 2 uzunluğunu yaparız ..

  • 1

    Bu, awk'a revize edilen satırı yazdırmasını söyler.

  • OFS=/

    Bu, awk'ye çıktıdaki alan ayırıcısı olarak eğik çizgi kullanmasını söyler.


Mükemmel yanıt, ayırıcı kullanmak için küçük değişiklik : awk -F/ '{for (i=1;i<NF;i++) $i=substr($i,1,1+($i~/^[.]/))(i==1||length($i)<2?"":"‥")} 1' OFS=/ <<<$PWDverir: /foo/bar/.config/wizard_magic/f‥/b‥/.c‥/wizard_magic
ideasman42

12

Sed'de oldukça kolay (dosya adlarında yeni satır olmadığı varsayılarak):

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

Awk'de daha az kolay çünkü geri referanslardan yoksun (Gawk hariç, ama beceriksiz bir sözdizimi ile):

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

Zsh dilinde (yolu ile $full_path):

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

2
IIRC, "backreferences", değiştirme dizesinde değil, kalıpta meydana gelen yakalama gruplarına yapılan göndermelerdir.
Rhymoid

@Rhymoid \1yedek dizede yok desende bir yakalama grubuna bir başvuru anlamına gelir. Geri başvuru, nerede kullanırsanız kullanın bir geri başvurudır.
Gilles 'SO- kötü olmayı bırak'

8

şöyle yapabilirsiniz:

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

/u/s/m/man1

ve işte bir 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/

Bu, işlevin aşağıda yaptığı şeylerin hepsini yapmaya oldukça yakındır. tildes ile kısaltma yapmaz veya $PWDfonksiyonun yaptığı gibi bir eğik çizgi için başa yerleştirmez (ve aslında, asla eğik çizgi yazdırmaz) ancak daha sonra ele alınabilir. null yol bileşenlerini ve tek noktaları işler ve ..vakaları ayıklar .

yukarıdakiyle aynı manyol verildiğinde cdyazdırılır:

u/s/m/man1

bununla başlayan ve yalnızca bir veya iki nokta olmayan her yol bileşeni için bir veya iki ekstra ön nokta yazdırır.

a ile başlayan bir yol bileşeni için birden fazla karakter yapmayı istediniz .. Bunu yapmak için her bileşenin yine de bireysel ilgi gerektireceğini düşündüm ve merak ettiğim için, değişiklik dizini olmadan kanonik bir yol üzerinde çalışmayı denedim. bazı deneme yanılma sonra nihayet doğru yapmanın tek yolu iki kez - geri ve ileri yapmak olduğuna karar verdi:

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:--}"
}

böylece hiçbir zaman dizini değiştirmez veya herhangi bir yol bileşeninin varlığını onaylamaya çalışmaz, ancak yinelenen /sınırlayıcıları sıkar ve /./tek noktalı bileşenleri tamamen bırakır ve /../çift ​​noktalı bileşenleri uygun şekilde işler .

zaman $IFSbazı ayarlandığında boşluk olmayan karakterine, iki veya daha fazla bir sekans $IFSkarakterlerin bir veya daha fazla boş alanları ile sonuçlanacaktır. bu yüzden ardışık çoklu eğik çizgiler null değerli argümanlar için işe yarar. aynı şey önde gelen bir $IFSkarakter için de geçerlidir . ve böylece set -- $1böler, eğer sonuç $1null ise, o zaman bir eğik çizgi ile başladı, başka, ${1:+$PWD}null değilse, o zaman eklerim $PWD. başka bir deyişle, eğer ilk argüman eğik çizgi ile başlamazsa, başlayacaktır $PWD. bu yol doğrulamaya geldiği kadar yakın .

aksi takdirde, ilk fordöngü, aşağıdaki gibi yol bileşenlerinin sırasını tekrar tekrar tersine çevirir:

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

... bunu yaparken herhangi bir tek noktalı veya boş bileşeni yok sayar ve ..bunun için ...

      1 .. 3
1     .. 3
      3
3

... ikinci geçiş bu efekti tersine çevirir ve bunu yaparken her bileşeni 2 nokta + karakter veya 1 nokta + karakter veya karakter olarak sıkıştırır .

bu yüzden varoluşundan bağımsız olarak kanonik bir yol üzerinde çalışmalıdır.

i ikinci döngüye biraz ekledi / çıkardı. şimdi setdaha az sıklıkta (her [!./]*bileşen için sadece bir kez ) ve kısa devre casedesen değerlendirmeleri çoğu zaman (daha önce bahsedilen desen sayesinde) ve karşı çağrı eşleştirme değerlendirmesi içerir ~. nihayet kanonik yolun tamamı veya bir ön kısmı (bütün bileşenlere bölünmüş olarak) eşleşebilirse ~, eşleşen bit çıkarılır ve değişmez bir yer ~değiştirilir. Bunu yapmak için, kısaltılmış yanında yolun tam bir kopyasını korumak zorunda kaldı (çünkü kısaltılmış yolu ~muhtemelen çok yararlı olmaz) , ve bu tutulur $3. sonwhiledöngü dalı yalnızca ~bir alt kümesi olarak eşleştirilirse çalıştırılır $3.

set -xizleme etkin olarak çalıştırırsanız çalışmasını izleyebilirsiniz.

$ (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
Güzel, ama gözlerim acıyor.
glenn jackman

1
@don_crissti - evet!
mikeserv

2

"Balık" Zsh tema Oh My Zsh bir Perl o Unicode desteği vardır sadece bunu yapmak için pasajı içerir:

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

1

Kısa adı mı yoksa komut satırınız için mi kullanmak istiyorsunuz?
Komut satırı için aşağıdaki önerilerim var:
Kabuğunuzdaki dosya tamamlama size yardımcı olmuyor mu?
Bazen şanslısınız ve özel bir şey yapmak zorunda değilsiniz:

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

İlgilendiğiniz bazı dizinleriniz olduğunda takma adları kullanabilirsiniz:

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"

Veya en sevdiğiniz dizinler için değişkenler ayarlayabilirsiniz

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

Bu seçeneklerin .bashrc (veya .profile) gibi tanımlanmış bir işlevle bunu çözmeye çalışmaktan daha mantıklı olduğunu düşünüyorum

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

ve bu işleve x'i harfleriniz arasında boşluklar olarak adlandırmak

 # cd /path/to
 x /p t

 # cd /tmp 
 x /t

 # cd /foo/bar/.config
 x /f b 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.