2009-10-07 24 views
7

Tôi đang cố gắng thiết lập hoàn thành thư mục trong tcsh và/hoặc bash (cả hai được sử dụng tại trang web của tôi) với một chút vặn: cho một lệnh cụ thể "foo" , Tôi muốn hoàn thành sử dụng một hàm tùy chỉnh để khớp với cụm từ đầu tiên/-delimited đến nút con thực tế, và sau đó làm theo hoàn thành thư mục bình thường cho bất kỳ cụm từ liên tiếp nào. Nó là một loại kết hợp của cdpath và hoàn thành, hoặc tôi giả sử một hình thức hoàn thành thư mục nơi mà điểm bắt đầu được điều khiển bởi kịch bản hoàn thành. Nó sẽ làm việc như sau:Tcsh và/hoặc hoàn thành thư mục bash với tiền tố gốc ẩn biến

$ foo xxx<TAB> 
(custom completion function produces choices it finds at arbitrary levels in the dir tree) 
xxxYYY xxxZZZ xxxBLAH ... 
foo xxxYYY/<TAB> 
(normal directory completion proceeds from this point on, to produce something like:) 
foo scene/shot/element/workspace/user/... 

Chúng tôi muốn làm điều này bởi vì chúng tôi có một cây phát triển sản xuất lớn (đây là một cơ sở sản xuất CGI), mà người dùng vỏ hiểu biết đang điều hướng và nhảy xung quanh trong tất cả các thời gian. Khiếu nại là các cấp trên của cây là cồng kềnh và dư thừa; họ chỉ cần tìm kiếm nhanh về cụm từ đầu tiên để tìm các lựa chọn "đầu" có thể và thực hiện hoàn thành thư mục từ đó. Nó có vẻ như hoàn thành lập trình có thể cung cấp một cách để làm điều này, nhưng nó được bật ra được khá khó nắm bắt.

Tôi đã thực hiện một số lần thử bash và tcsh tùy chỉnh để thực hiện việc này, nhưng gần nhất tôi nhận được là một dạng "hoàn thành từ", nơi người dùng phải xử lý các cấp thư mục như các từ riêng biệt với dấu cách (ví dụ: foo scene/shot/element/không gian làm việc/...). Tôi có thể tiếp tục hack vào các kịch bản hiện tại của mình - nhưng tôi đã tự hỏi nếu có điều gì đó tôi không hiểu - đây là lần đầu tiên tôi hoàn thành chương trình, và các tài liệu và ví dụ khá mỏng trong sách vỏ và trên internet . Nếu có bất kỳ guru hoàn thành ra khỏi đó có thể giúp tôi đi đúng hướng, tôi sẽ đánh giá cao nó.

FWIW: đây là những gì tôi đã có cho đến nay (trong tcsh đầu tiên, sau đó bash). Lưu ý rằng root tĩnh '/ root/sub1/sub2/sub3' chỉ là một trình giữ chỗ cho một hàm tìm kiếm có thể tìm các kết quả khớp khác nhau ở các mức khác nhau. Nếu tôi có thể làm điều đó để làm việc, tôi có thể phụ trong tính năng tìm kiếm sau này. Một lần nữa, cả hai ví dụ thực hiện hoàn thành từ, yêu cầu người dùng nhập một khoảng trắng sau mỗi cụm từ khớp nhau (tôi cũng phải xóa các khoảng trống trong hàm để tạo đường dẫn thực tế, yuck!)

VÍ DỤ TCSH (lưu ý hàm là thực sự là một kịch bản bash):

complete complete_p2 '[email protected]*@`./complete.p2.list.bash $:1 $:2 $:3 $:4 $:5 $:6 $:7 $:8 $:9`@@' 

#!/bin/bash --norc 

# complete.p2.list.bash - Completion prototype "p2" for shotc command 

# Remove spaces from input arguments 
ppath=`echo [email protected] | sed -e 's/ //g'` 

# Print basenames (with trailing slashes) of matching dirs for completion 
ls -1 -d /root/sub1/sub2/sub3/$ppath* 2>/dev/null | sed -e 's#^.*/##' | awk '{print $1 "/"}' 

bASH VÍ DỤ:

_foo() 
{ 
    local cur prev opts flist 
    COMPREPLY=() 
    cur="${COMP_WORDS[COMP_CWORD]}" 
    prev="${COMP_WORDS[COMP_CWORD-1]}" 

    # Get all command words so far (omit command [0] element itself), remove spaces 
    terms=`echo ${COMP_WORDS[@]:1} | sed -e 's/ //g'` 

    # Get basenames (with trailing slashes) of matching dirs for completion 
    flist=`ls -1 -d /root/sub1/sub2/sub3/${terms}* 2>/dev/null | sed -e 's#^.*/##' | awk '{print $1 "/"}' | xargs echo` 

    COMPREPLY=($(compgen -W "${flist}" ${cur})) 
    return 0 
} 
complete -F _foo foo 
+0

Biến môi trường CDPATH của bash có phù hợp với bạn không? – Cascabel

+0

Tôi nhìn vào CDPATH, nhưng nó không hoạt động VỚI hoàn thành. Bạn có thể "cd name", nhưng không phải "cd name ..." Tôi thực sự cần hoàn thành. –

+0

Tôi nghĩ rằng hoàn thành bash (kịch bản thêm đầy đủ các tính năng hữu ích mà tàu với bash trên hầu hết các distro) bao gồm một định nghĩa cập nhật hoàn thành cd bằng cách sử dụng CDPATH. – Cascabel

Trả lời

0

Bạn chỉ có thể tạo ra một liên kết tượng trưng đến nút thú vị đầu tiên trong cây. Tôi đã làm điều này trong quá khứ khi tôi không thể bị làm phiền tự động hoàn thành cây thư mục lớn.

+0

Thật không may là "nút thú vị đầu tiên" có thể thay đổi rất nhiều. Khi người dùng dành nhiều thời gian ở các phần khác nhau của cây và nhảy xung quanh các subtrees, không có dự đoán mức độ nào họ có thể neo một nỗ lực hoàn thành. Trừ khi chúng tôi liên kết mọi cấp độ (có vẻ cồng kềnh và khó sử dụng), tôi không chắc chắn các liên kết sẽ giải quyết nó. –

4

Điều này có vẻ như nó có thể làm những gì bạn đang tìm kiếm:

_foo() 
{ 
    local cur prev opts flist lastword new 
    COMPREPLY=() 
    cur="${COMP_WORDS[COMP_CWORD]}" 
    prev="${COMP_WORDS[COMP_CWORD-1]}" 
    lastword="${COMP_WORDS[@]: -1}" 

    if [[ $lastword =~/]] 
    then 
     new="${lastword##*/}"  # get the part after the slash 
     lastword="${lastword%/*}" # and the part before it 
    else 
     new="${lastword}" 
     lastword="" 
    fi 

    flist=$(command find /root/sub1/sub2/sub3/$lastword \ 
     -maxdepth 1 -mindepth 1 -type d -name "${new}*" \ 
     -printf "%f\n" 2>/dev/null) 

    # if we've built up a path, prefix it to 
    # the proposed completions: ${var:+val} 
    COMPREPLY=($(compgen ${lastword:+-P"${lastword}/"} \ 
     -S/ -W "${flist}" -- ${cur##*/})) 
    return 0 
} 
complete -F _foo -o nospace foo 

Ghi chú:

  • Tôi nghĩ rằng một trong những chìa khóa là nospace tùy chọn
  • tôi cảm thấy như tôi đã tạo lại một bánh xe ở đâu đó trong chức năng ở trên, có thể là không sử dụng $COMP_POINT
  • Bạn chưa (ít nhất) sử dụng $prev (luôn duy trì giá trị "foo" trong chức năng của tôi)
  • Độ khó và chức năng có thể được cải thiện bằng cách sử dụng $() thay vì backticks
  • Bạn nên sử dụng để ngăn chặn command bí danh hoạt động và như vậy: flist=$(command ls -1 -d...
  • Tôi đang sử dụng find thay của ls vì nó phù hợp hơn
  • bạn có thể thêm dấu gạch chéo bằng -S/ với compgen thay vì awk lệnh của bạn
  • bạn có thể sử dụng $cur thay vì 0.123.kể từ khi bạn không cần phải loại bỏ không gian, nhưng tôi đang sử dụng $lastword$new (hai biến mới)
  • Đó không phải là cần thiết để sử dụng xargs echo từ một mảng với dòng mới hoạt động tốt
  • tôi đã không kiểm tra này với các tên thư mục có dấu cách hoặc dòng mới trong chúng
+0

Cảm ơn Dennis! Đó là rất gần với những gì tôi đang tìm kiếm. Tôi có thể sử dụng nó với một vài chỉnh sửa, hoặc thậm chí là như vậy. Tôi sẽ xem nếu tôi có thể áp dụng một phương pháp tương tự để tcsh là tốt. –

+0

Tôi nghĩ rằng tôi đã khá đơn độc để cần các kịch bản hoàn thành phức tạp :) +1 cho nỗ lực mã! – neuro

1

giải pháp của tôi, được thừa nhận là một búa búa 800, là viết một kịch bản perl để xử lý hoàn thành theo cách tôi muốn. tcsh trong ...

complete cd 'p|1|`complete_dirs.pl $:1 $cdpath`|/' 

#!/usr/bin/perl 

my $pattern = shift @ARGV; 
my @path = @ARGV; 
my @list; 

if ($pattern =~ m!^(/.+/|/)(.*)!) { 
    @list = &search_dir($1,$2,undef); 
} elsif ($pattern =~ m!(.+/|)(.*)!) { 
    my $dir; foreach $dir ('.',@path) { 
    push(@list,&search_dir("$dir/$1",$2,$1)); 
    } 
} 
if (@list) { 
    @list = map { &quote_path($_) } @list; 
    print join(' ',@list), "\n"; 
} 

sub search_dir { 
    my ($dir,$pattern,$prefix) = @_; 
    my @list; 

    if (opendir(D,$dir)) { 
    my $node; while ($node = readdir(D)) { 
     next  if ($node =~ /^\./); 
     next unless ($node =~ /^$pattern/); 
     next unless (-d "$dir$node"); 

     my $actual; if (defined $prefix) { 
     $actual = "$prefix$node"; 
     } elsif ($dir =~ m!/$!) { 
     $actual = "$dir$node"; 
     } else { 
     $actual = "$dir/$node"; 
     } 
     push(@list,$actual); 
    } 
    closedir(D); 
    } 
    return @list; 
} 
sub quote_path { 
    my ($string) = @_; 

    $string =~ s!(\s)!\\$1!g; 
    return $string; 
} 
0

Các giải pháp cơ bản tcsh là rất dễ dàng để thực hiện như sau:

complete foo '[email protected]*@D:/root/sub1/sub2/[email protected]' 

Không có bash script phụ thuộc yêu cầu. Tất nhiên thư mục cơ sở là hardwired trong ví dụ này.

1

Vì vậy, đây là giải pháp tcsh. Thêm toàn bộ $ cdpath tìm kiếm các thư mục tự động hoàn thành. Lệnh hoàn chỉnh là:

complete cd '[email protected]/@[email protected]''[email protected]@`source $HOME/bin/mycdpathcomplete.csh $:1`@/@' 

Tôi là một chút của một tcsh hack, do đó, thao tác chuỗi là một chút thô. Nhưng nó hoạt động với chi phí không đáng kể ... mycdpathcomplete.csh trông giống như sau:

#!/bin/tcsh -f 

set psuf="" 
set tail="" 

if ($1 !~ "*/*") then 
    set tail="/$1" 
else 
    set argsplit=(`echo "$1" | sed -r "[email protected](.*)(/[^/]*\')@\1 \[email protected]"`) 
    set psuf=$argsplit[1] 
    set tail=$argsplit[2] 
endif 

set mycdpath=(. $cdpath) 
set mod_cdpath=($mycdpath) 

if ($psuf !~ "") then 
    foreach i (`seq 1 1 $#mycdpath`) 
     set mod_cdpath[$i]="$mycdpath[$i]/$psuf" 
     if (! -d $mod_cdpath[$i]) then 
      set mod_cdpath[$i]="" 
     endif 
    end 
endif 

# debug statements 
#echo "mycdpath = $mycdpath" 
#echo "mod_cdpath = $mod_cdpath" 
#echo "tail = $tail" 
#echo "psuf = $psuf" 

set matches=(`find -L $mod_cdpath -maxdepth 1 -type d -regex ".*${tail}[^/]*" | sed -r "[email protected]*(/?${psuf}${tail}[^/]*)\'@\[email protected]" | sort -u`) 

# prune self matches 
if ($psuf =~ "") then 
    foreach match (`seq 1 1 $#matches`) 
     set found=0 
     foreach cdp ($mod_cdpath) 
      if (-e "${cdp}${matches[$match]}") then 
       set found=1; 
       break; 
      endif 
     end 
     if ($found == 0) then 
      set matches[$match]="" 
     else 
      set matches[$match]=`echo "$matches[$match]" | sed -r "[email protected]^/@@"` 
     endif 
    end 
endif 

echo "$matches" 
Các vấn đề liên quan