วิธีย่อ / path / to / file ไปยัง / p / t / file


9

ฉันกำลังมองหาซับในที่สง่างาม (เช่นawk) ที่จะทำให้สตริงของเส้นทาง Unix สั้นลงโดยใช้อักขระตัวแรกของผู้ปกครองแต่ละคน / ระดับกลาง แต่เป็นชื่อเต็ม ง่ายต่อการแสดงโดยตัวอย่าง:

  • /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ย่อแทน จุดมักพบเห็นได้ทั่วไปในสารบบโดยเฉพาะว่าเป็นเบาะแสเล็ก ๆ ที่คุณควรมอง
Chris H

นอกจากสิ่งที่ @ChrisH พูดแล้ว.ปกติหมายถึง "ไดเรกทอรีปัจจุบัน" ดังนั้น/f/b/./wizard_magicเป็นเช่นเดียวกับ/f/b/wizard_magicเพราะองค์ประกอบเส้นทาง./การบีบอัดเพื่อเป็นองค์ประกอบเส้นทางที่ว่างเปล่า
CVn

ทำไมคุณต้องการมัน คุณไม่สามารถใช้การเติมข้อความอัตโนมัติที่ชาญฉลาดในเชลล์เชิงโต้ตอบของคุณได้ (อาจเปลี่ยนเชลล์ให้เป็นอะไรที่เพียงพอ)
Basile Starynkevitch

คำตอบ:


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

แก้ไข 1: การใช้อักขระสองตัวสำหรับชื่อจุด

รุ่นนี้ย่อชื่อไดเรกทอรีเป็นหนึ่งตัวอักษรยกเว้นชื่อที่ขึ้นต้นด้วย.ตัวย่อสองตัวอักษร:

$ 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: ในรุ่นปรับปรุงที่เราให้ความยาวของ substring 2 .เมื่อสนามเริ่มต้นด้วย

  • 1

    สิ่งนี้บอกให้ awk พิมพ์บรรทัดที่แก้ไข

  • OFS=/

    สิ่งนี้บอกให้ awk ใช้เครื่องหมายสแลชเป็นตัวคั่นฟิลด์ในเอาต์พุต


คำตอบที่ยอดเยี่ยมการแก้ไขเล็กน้อยเพื่อใช้ตัวแยก: 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
คิด man42 42

12

ค่อนข้างง่ายใน sed (สมมติว่าไม่มีบรรทัดใหม่ในชื่อไฟล์):

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

ง่ายน้อยลงใน awk เพราะมันขาด backreferences (ยกเว้นใน Gawk แต่ด้วยไวยากรณ์ซุ่มซ่าม):

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 "backreferences" เป็นการอ้างอิงถึงกลุ่มการดักจับที่เกิดขึ้นในรูปแบบไม่ใช่ในสตริงการแทนที่
Rhymoid

@Rhymoid \1ในสตริงทดแทนไม่ได้หมายความว่าการอ้างอิงไปยังกลุ่มการบันทึกในรูปแบบ การอ้างอิงย้อนกลับคือการอ้างอิงย้อนกลับไม่ว่าคุณจะใช้งานที่ไหน
Gilles 'หยุดความชั่วร้าย' ใน

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อักขระสองตัวหรือมากกว่านั้นจะส่งผลให้เกิดช่องว่างอย่างน้อยหนึ่งช่อง เครื่องหมายทับที่ต่อเนื่องกันหลายตัวทำงานกับอาร์กิวเมนต์ที่มีค่าเป็น null เช่นเดียวกับ$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 จุดถ่าน +หรือถ่าน

ดังนั้นจึงควรทำงานกับเส้นทางที่ยอมรับโดยไม่คำนึงถึงการดำรงอยู่

ฉันเพิ่ม / ลบออกเล็กน้อยในลูปที่สอง ตอนนี้sets มักจะน้อย(เพียงครั้งเดียวสำหรับแต่ละ[!./]*องค์ประกอบ)และสั้นวงจรcaseการประเมินผลรูปแบบมากที่สุดของเวลา(ขอบคุณรูปแบบดังกล่าวข้างต้น)~และรวมถึงหางโทรตรงกับการประเมินผล หากส่วนทั้งหมดหรือส่วนนำ(แบ่งตามส่วนประกอบทั้งหมด)ของเส้นทางที่ยอมรับในที่สุดสามารถจับ~คู่บิตที่ตรงกันจะถูกถอดออกและตัวอักษร~จะถูกแทนที่ เพื่อที่จะทำเช่นนี้และฉันได้เพื่อรักษาฉบับเต็มของเส้นทางข้างที่ยากเช่นกัน(เพราะตรงกับเส้นทางที่ยากที่จะ~อาจจะไม่เป็นประโยชน์มาก)และอื่น ๆ $3นี้จะถูกเก็บไว้ใน สุดท้ายwhilebranch branch จะทำงานหาก~ถูกจับคู่เป็นเซตย่อย$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จาก Oh My zsh มี Perl snippet จะทำเพียงแค่ว่ามีการสนับสนุน Unicode:

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

1

คุณต้องการที่จะมีชื่อย่อหรือใช้มันสำหรับ commandline ของคุณ?
สำหรับ commandline ฉันมีข้อเสนอแนะดังต่อไปนี้:
ไฟล์ของคุณไม่ได้ช่วยคุณได้หรือไม่
บางครั้งคุณโชคดีและไม่ต้องทำอะไรพิเศษ:

# /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 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.