7

少しひねりを加えて、tcshおよび/またはbash(両方とも私のサイトで使用されています)でディレクトリ補完を設定しようとしています。特定のコマンド「foo」について、補完にカスタム関数を使用して一致させたいと思います/ で区切られた最初の用語を実際のサブツリー ノードに追加し、その後、後続の用語については通常のディレクトリ補完に従います。これは、cdpath と補完の組み合わせのようなものです。または、補完スクリプトによって開始点が制御されるディレクトリ補完の形式だと思います。次のように動作します。

$ 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/...

私たちは大規模な製品開発ツリー (これは CGI 生産施設です) を持っており、シェルに精通したユーザーが常にナビゲートしたり飛び回ったりしているため、これを行いたいと考えています。不満は、ツリーの上位レベルが扱いにくく冗長であることです。可能な「頭」の選択肢を見つけ、そこからディレクトリ補完を行うために、最初の用語をすばやく検索する必要があります。プログラマブル補完がこれを行う方法を提供できるように思えますが、それはかなりとらえどころのないものであることが判明しています.

これを行うために、カスタムの bash と tcsh の補完を何度か試みましたが、最も近いものは、ユーザーがディレクトリ レベルをスペースを含む個別の単語として扱わなければならない「単語補完」の形式です (例: foo scene/ shot /要素/ワークスペース/ ...)。現在のスクリプトをハッキングし続けることもできますが、何か理解できないことがあるのだろうかと思っていました.これはプログラム補完の最初の試みであり、ドキュメントと例はシェルブックとインターネットでかなり薄いです. . 私を正しい軌道に乗せることができる補完の達人がそこにいるなら、私はそれを感謝します.

FWIW:これが私がこれまでに得たものです(最初にtcshで、次にbashで)。静的ルート「/root/sub1/sub2/sub3」は、さまざまなレベルでさまざまな一致を見つける検索機能の単なるプレースホルダーであることに注意してください。それを機能させることができれば、後で検索機能をサブスクライブできます。繰り返しますが、どちらの例も単語補完を行います。これには、一致する各用語の後にユーザーがスペースを入力する必要があります (実際のパスを構築するために、関数内のスペースも削除する必要があります!)

TCSH の例 (関数は実際には bash スクリプトであることに注意してください):

complete complete_p2 'C@*@`./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 $@ | 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 の例:

_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
4

5 に答える 5

4

これはあなたが探していることをするかもしれないようです:

_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

ノート:

  • nospaceキーの1つはオプションだと思います
  • おそらく使用しないことで、上記の関数のどこかで車輪を再発明したような気がします$COMP_POINT
  • あなたは(まだ、少なくとも)使用していません$prev(これは常に私の関数で値「foo」を維持します)
  • $()バッククォートの代わりに使用することで、読みやすさと機能性を向上させることができます
  • commandエイリアスなどの実行を防ぐために使用する必要があります。flist=$(command ls -1 -d...
  • より適しているため、find代わりに使用していますls
  • コマンドの代わりに-S/withを使用してスラッシュを追加できますcompgenawk
  • $curスペースを取り除く必要がないので代わりに使用できます$termsが、私は$lastwordand $new(2 つの新しい変数)を使用しています。
  • xargs echo改行を含む配列は正常に機能するため、使用する必要はありません
  • スペースや改行を含むディレクトリ名でこれをテストしていません
于 2009-10-08T07:27:34.643 に答える
1

確かに800ポンドのハンマーである私の解決策は、私が望むように補完を処理するperlスクリプトを書くことでした。tcshで...

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;
}
于 2010-02-10T22:27:21.130 に答える
1

したがって、ここに tcsh ソリューションがあります。オートコンプリート ディレクトリを検索する完全な $cdpath を追加します。完全なコマンドは次のとおりです。

complete cd 'C@/@d@''p@1@`source $HOME/bin/mycdpathcomplete.csh $:1`@/@'

私は少し tcsh ハックなので、文字列操作は少し粗雑です。しかし、無視できるオーバーヘッドで動作します... mycdpathcomplete.csh は次のようになります。

#!/bin/tcsh -f

set psuf=""
set tail=""

if ( $1 !~ "*/*" ) then
    set tail="/$1"
else
    set argsplit=(`echo "$1" | sed -r "s@(.*)(/[^/]*\')@\1 \2@"`)
    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 "s@.*(/?${psuf}${tail}[^/]*)\'@\1@" | 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 "s@^/@@"`
        endif
    end
endif

echo "$matches"
于 2017-06-08T19:41:00.383 に答える
0

基本的なtcshソリューションは、次のように実装するのが非常に簡単です。

    complete foo 'C@*@D:/root/sub1/sub2/sub3@'

bashスクリプトの依存関係は必要ありません。もちろん、この例ではベース ディレクトリが固定されています。

于 2012-05-17T20:50:09.967 に答える
0

ツリー内の最初の興味深いノードへのシンボリック リンクを作成できます。過去に、大きなディレクトリ ツリーの自動補完に煩わされたときに、これを行ったことがあります。

于 2009-10-07T03:13:42.763 に答える