Czy można skonfigurować sposób, w jaki bash uzupełnia nazwy katalogów?

8

Chciałbym poinstruować bash, aby użył specjalnej metody do zakończenia niektórych nazw katalogów. Na przykład, bash wywołałby mój program, aby wykonać uzupełnienie, jeśli ścieżka zaczyna się od „$$”, a w przeciwnym razie wykonałby ukończenie.

Czy to w ogóle możliwe? Jak byś to wdrożył?

Bounty : Naprawdę doceniłbym odpowiedź na to pytanie. Celem jest umożliwienie autojumpowi uzupełnienia ścieżek dla wszystkich poleceń, gdy użytkownik uruchomi je z określonym prefiksem. Na przykład podczas kopiowania pliku z odległego katalogu możesz wpisać:

cp $$patern + <Tab>

i autojump zakończy się

cp /home/user/CompliCatedDireCTOry/long/path/bla/bla

i musisz tylko dodać miejsce, w którym chcesz umieścić plik. Oczywiście mogę użyć komentarza otta, aby dodać go do kilku konkretnych poleceń, ale jeśli ktoś ma lepszy pomysł, byłbym bardzo wdzięczny.

Peltier
źródło
Zobacz sekcję „Programowalne zakończenie” na stronie man bash.
Jeśli głosujesz za zamknięciem mojego pytania, wyjaśnij dlaczego.
Peltier
2
„complete” obsługuje wzorzec „-G”, aby dopasować $$ na początku, a „-F func”, aby wywołać własną funkcję, ale do działania potrzebna jest co najmniej jedna nazwa polecenia.
Dlaczego nie użyć po prostu zmiennej środowiskowej? Na przykład: cp $ prefix / file / path / to / dest /
Xenoactive
@ Xenoactive: ponieważ autojump jest w pełni zautomatyzowany i działa na więcej niż jednej ścieżce. Używanie ręcznie ustawianych zmiennych środowiskowych nie pomaga. A może masz potężny pomysł, którego nie rozumiem?
Peltier

Odpowiedzi:

3

Możesz to zrobić, zastępując domyślne powiązanie dla TAB (^ i). Najpierw musisz przesłonić powiązanie TAB, następnie musisz zbudować funkcję, która wywołuje twoje polecenie, na koniec musisz pobrać dane wyjściowe z tego polecenia i zaktualizować zmienną zawierającą bieżący wiersz polecenia.

Ta funkcja pobiera bieżący wiersz poleceń i zmienia ostatnie dwa znaki na „huugs”.

function my_awesome_tab_completion_function () {
  set -- $READLINE_LINE
  command="$1"
  shift
  argument="$*"
  argument_length=$(echo -n $argument | wc -c)
  if echo $argument | grep '^$$' >/dev/null 2>&1; then
    new_argument=$(echo $argument | sed 's/..$/huugs/') # put your autojump here
  else
    new_argument=$(compgen -d $argument)
  fi
  new_argument_length=$(echo -n $new_argument | wc -c)
  READLINE_POINT=$(( $new_argument_length - $argument_length + $READLINE_POINT ))
  READLINE_LINE="$command $new_argument"
}

Na przykład prawdopodobnie chcesz zmienić linię new_argument, aby wyglądała następująco:

  new_argument=$(autojump $argument)

Teraz przesłoń wiązanie ^ i:

$ bind -x '"\C-i"':'my_awesome_tab_completion_function'

Teraz sprawdź, czy to działa:

$ cd /ro<TAB>
changes my command to:
$ cd /root

więc normalne zakończenie nadal działa, możesz przetestować część $$, wykonując cd $$ ... itd

Jeśli napotkasz problemy, włącz tryb szczegółowy:

$ set -x

Wydrukuje wszystko, co robi funkcja.

Przetestowałem to na Ubuntu 11 przy użyciu wersji bash 4.2.8 (1) (domyślnie).

wielomian
źródło
Wydaje mi się, że działa to tylko na pierwszym parametrze polecenia i może również robić dziwne rzeczy, jeśli karta jest używana w nieoczekiwany sposób (na przykład po $$$ lub po nazwie polecenia), czy też się mylę?
harrymc
Cóż, przekazuje $ *, więc będzie działał na więcej niż tylko pierwszym parametrze (nie znam w ogóle autojumpa, więc nie jestem pewien, czy może przyjąć wiele argumentów). Możesz przejść do końca i wysłać tylko ostatnią do autojumpa lub użyć READLINE_POINT, aby ustalić, gdzie kursor znajduje się wśród argumentów polecenia i wysłać tylko to „słowo” do autojumpa.
wielomian
Twoje rozwiązanie jest interesujące, ale musisz przyznać, że przejęcie klawisza Tab jest nieco ekstremalne. Nie ma możliwości, aby można było przewidzieć wszystkie możliwe przypadki.
harrymc
Dziękuję za odpowiedź, to miłe alternatywne rozwiązanie, które rozwiązuje część „wszystkich poleceń” mojego pytania. Zgadzam się z Harrymcem, że jest to trochę ekstremalne, ale ... Myślę, że jeśli nikt nie wymyśli lepszego rozwiązania, spróbuję i zobaczę, czy jest to wykonalne.
Peltier,
Tak, zgadzam się też z Harrymcem, ale starałem się odpowiedzieć na zadane pytanie. Osobiście po prostu powiązałbym to z innym klawiszem sterującym, takim jak ^ G, lub czymś innym, więc mogłem zostawić kartę w spokoju, ale nadal korzystać z tej funkcji.
wielomian
1

Procedurę ukończenia bash można zaprogramować jako skrypt powłoki.

Oto przykład skrypt powłoki, który zastąpi w dowolnym parametrze $$[Tab]przez my replacement string, ale tylko dla określonego polecenia mycommandi tylko wtedy, gdy parametr jest dokładnie „$$”:

_mycomplete()
{
        if [ ${COMP_WORDS[COMP_CWORD]} == \$\$ ]
        then
                COMPREPLY='my replacement string'
        fi
}
complete -o default -o bashdefault -F _mycomplete mycommand

Musisz uruchomić skrypt, aby uruchomić Bash source <file-name>(lub polecenie kropki), aby zaczął działać, a następnie:

mycommand $$[Tab] -> mycommand my replacement string
mycommand $$$[Tab] -> mycommand $$$ (beep)
mycommand whatever[Tab] -> (will complete "whatever" in the normal bash manner)

Aby zawsze działało dla niektórych lub wszystkich użytkowników, włącz to do jednej z procedur profilu bash .

Problem z completepoleceniem polega na tym, że będzie on działał tylko dla jednej lub więcej nazw poleceń, które są określone jako parametry. Można po prostu podać listę wszystkich poleceń, które mogą być używane przez użytkowników lub w rozpaczliwych przypadkach rozszerzać /bin/* /usr/bin/* ~/bin/*.

Testowane na CentOS 5.5.

Ten prosty skrypt jest oparty na źródłach, które wymieniłem w drugiej odpowiedzi - tej, która została usunięta przez moderatora studiohacka. Jeśli jesteś zainteresowany, poproś go, aby go cofnął.

harrymc
źródło
Dzięki za odpowiedź. Moje pytanie tak naprawdę nie dotyczyło tego, ponieważ nowe było możliwe, ale nie wykonam wszystkich poleceń. Myślę jednak, że jeśli rozwiązanie wielomianu nie okaże się możliwe do zaakceptowania, zaimplementuję coś takiego i spróbuję celować w najczęstsze polecenia.
Peltier