Cách viết tắt / path / to / file thành / p / t / file


9

Tôi đang tìm kiếm một lớp lót thanh lịch (ví dụ awk:) sẽ rút ngắn một chuỗi đường dẫn Unix bằng cách sử dụng ký tự đầu tiên của mỗi cấp độ cha mẹ / trung gian, nhưng tên cơ sở đầy đủ. Dễ dàng hiển thị hơn bằng các ví dụ:

  • /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
    Trong những điểm tốt của @ MichaelKjorling và @ChrisH bên dưới, ví dụ này cho thấy cách chúng tôi có thể hiển thị hai ký tự đầu tiên khi ký tự đầu tiên là một dấu chấm.

Một gợi ý (tôi không biết trường hợp sử dụng của bạn): viết tắt thay vào đó /f/b/.c/wizard_magic. Dấu chấm thường rất phổ biến trong một thư mục cụ thể đến mức là một đầu mối rất nhỏ đến nơi bạn nên tìm kiếm.
Chris H

Bên cạnh những gì @ChrisH đã nói, .thông thường chỉ có nghĩa là "thư mục hiện tại". Vì vậy, /f/b/./wizard_magicgiống như /f/b/wizard_magicbởi vì phần tử đường dẫn ./nén thành một phần tử đường dẫn trống.
một CVn

Tại sao bạn cần điều đó? Bạn không thể sử dụng một số tự động hoàn thành thông minh trong vỏ tương tác của mình (có thể thay đổi vỏ của bạn thành thứ gì đó phù hợp)
Basile Starynkevitch

Câu trả lời:


7

Đối với tệp thử nghiệm này:

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

Các chữ viết tắt có thể được tạo bằng mã awk này:

$ 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: Sử dụng hai ký tự cho tên dấu chấm

Phiên bản này viết tắt tên thư mục thành một ký tự trừ các tên bắt đầu bằng .chữ viết tắt thành hai ký tự:

$ 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

Làm thế nào nó hoạt động

  • -F/

    Điều này cho awk sử dụng dấu gạch chéo làm dấu tách trường trên đầu vào.

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

    Vòng lặp này trên mỗi trường, ngoại trừ trường cuối cùng và thay thế nó chỉ bằng ký tự đầu tiên.

    EDIT1: Trong phiên bản sửa đổi, chúng tôi tạo độ dài của chuỗi con 2 khi trường bắt đầu bằng ..

  • 1

    Điều này nói với awk để in dòng sửa đổi.

  • OFS=/

    Điều này cho awk sử dụng dấu gạch chéo làm dấu tách trường trên đầu ra.


Câu trả lời tuyệt vời, sửa đổi nhỏ để sử dụng dấu phân cách: awk -F/ '{for (i=1;i<NF;i++) $i=substr($i,1,1+($i~/^[.]/))(i==1||length($i)<2?"":"‥")} 1' OFS=/ <<<$PWDđưa ra: /foo/bar/.config/wizard_magic/f‥/b‥/.c‥/wizard_magic
ideaman42

12

Khá dễ dàng trong sed (giả sử không có dòng mới trong tên tệp):

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

Ít dễ dàng hơn trong awk vì nó thiếu phản hồi (ngoại trừ trong Gawk, nhưng với cú pháp vụng về):

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

Trong zsh (với đường dẫn trong $full_path):

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

2
IIRC, "backreferences" là các tham chiếu để chụp các nhóm xảy ra trong mẫu, không phải trong chuỗi thay thế.
Rhymoid

@Rhymoid \1trong chuỗi thay thế nghĩa là một tham chiếu đến một nhóm chụp trong mẫu. Backreference là một backreference cho dù bạn sử dụng nó ở đâu.
Gilles 'SO- ngừng trở nên xấu xa'

8

bạn có thể làm điều đó như:

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

/u/s/m/man1

và đây là 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/

điều đó khá gần với việc thực hiện tất cả những thứ tương tự mà hàm thực hiện bên dưới. nó không viết tắt bằng dấu ngã hoặc chèn $PWDvào đầu để không gạch chéo hàng đầu như chức năng thực hiện (và trên thực tế, không bao giờ in dấu gạch chéo hàng đầu) nhưng sau đó có thể được xử lý. nó xử lý các thành phần đường dẫn null, và các dấu chấm đơn và loại bỏ ..các trường hợp.

đưa ra cùng một manđường dẫn như cdtrên nó in:

u/s/m/man1

nó cũng sẽ in thêm một hoặc hai dấu chấm dẫn đầu cho mỗi thành phần đường dẫn bắt đầu bằng và không chỉ là một hoặc hai dấu chấm.

bạn đã hỏi về việc thực hiện nhiều hơn một ký tự cho một thành phần đường dẫn bắt đầu bằng a .. Để làm điều đó, tôi nghĩ rằng mỗi thành phần sẽ cần sự chú ý riêng lẻ, và vì tôi tò mò, tôi đã cố gắng tìm ra một con đường chính tắc mà không cần thư mục thay đổi. sau một vài lần thử và sai, cuối cùng tôi đã quyết định cách duy nhất để làm đúng là làm điều đó hai lần - ngược và tiến:

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

để không bao giờ thay đổi thư mục hoặc cố gắng xác nhận sự tồn tại của bất kỳ thành phần đường dẫn nào, nhưng nó sẽ nén hoàn toàn các /dấu phân cách lặp lại và loại bỏ /./hoàn toàn các thành phần dấu chấm đơn và xử lý /../các thành phần hai chấm một cách thích hợp.

khi $IFSđược đặt thành một số tự không phải khoảng trắng , một chuỗi gồm hai hoặc nhiều $IFSký tự sẽ dẫn đến một hoặc nhiều trường null. do đó, nhiều dấu gạch chéo liên tiếp có tác dụng với các đối số có giá trị null. điều tương tự cũng đúng với một $IFSnhân vật hàng đầu . và vì vậy khi set -- $1chia tách, nếu kết quả $1là null thì nó bắt đầu bằng dấu gạch chéo, nếu không, ${1:+$PWD}nếu không phải là null, thì tôi chèn $PWD. nói cách khác, nếu đối số đầu tiên không bắt đầu bằng dấu gạch chéo, nó sẽ được $PWDthêm vào trước. đó là gần như điều này để xác nhận đường dẫn .

mặt khác, forvòng lặp đầu tiên đệ quy đảo ngược thứ tự của các thành phần đường dẫn, như:

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

... Trong khi làm như vậy, nó bỏ qua mọi thành phần dấu chấm đơn hoặc null và đối với ..nó ...

      1 .. 3
1     .. 3
      3
3

... đường chuyền thứ hai đảo ngược hiệu ứng này và trong khi thực hiện, nó nén từng thành phần thành 2 chấm + char hoặc 1 chấm + char hoặc char .

vì vậy nó nên hoạt động theo một đường dẫn chính tắc bất kể sự tồn tại.

tôi đã thêm / bớt một chút vào vòng lặp thứ hai. bây giờ nó setít thường xuyên hơn (chỉ một lần cho mỗi [!./]*thành phần)casehầu hết thời gian đánh giá mẫu ngắn mạch (nhờ vào mẫu đã nói ở trên) và bao gồm cả đánh giá so khớp cuộc gọi đuôi ~. nếu tất cả hoặc một phần đầu (như được chia trên toàn bộ các thành phần) của đường dẫn chính tắc cuối cùng có thể khớp ~, bit khớp sẽ bị loại bỏ và một chữ ~sẽ được thay thế. để làm điều này, tôi đã phải duy trì một bản sao đầy đủ của đường dẫn bên cạnh chữ viết tắt (vì khớp với đường dẫn viết tắt ~có lẽ sẽ không hữu ích lắm) , và vì vậy nó được giữ nguyên $3. cuối cùngwhilenhánh vòng lặp chỉ được chạy nếu ~được khớp như một tập hợp con của $3.

nếu bạn chạy nó với set -xdấu vết được kích hoạt, bạn có thể xem nó hoạt động.

$ (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
Thật tuyệt, nhưng mắt tôi đau.
glenn jackman

1
@don_crissti - vâng!
mikeerv

2

Các "tanh" Zsh chủ đề từ Oh My Zsh chứa một đoạn mã Perl để làm việc đó có hỗ trợ Unicode:

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

1

Bạn có muốn có tên ngắn hoặc sử dụng nó cho dòng lệnh của bạn?
Đối với dòng lệnh tôi có các đề xuất sau:
Không hoàn thành tệp trong trình bao của bạn có giúp bạn không?
Đôi khi bạn may mắn và không phải làm điều gì đặc biệt:

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

Khi bạn chỉ có một số thư mục bạn quan tâm, bạn có thể sử dụng bí danh:

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"

Hoặc bạn có thể thiết lập các biến cho các thư mục yêu thích của bạn

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

Tôi nghĩ các tùy chọn này có ý nghĩa hơn là cố gắng giải quyết vấn đề này bằng một hàm được định nghĩa trong .bashrc (hoặc .profile) như

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

và gọi hàm này là x với khoảng trắng giữa các chữ cái của bạn:

 # cd /path/to
 x /p t

 # cd /tmp 
 x /t

 # cd /foo/bar/.config
 x /f b 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.