Jak użyć rozszerzenia wstecznego do wypełnienia arglisty?

11

Z pomocy :help backtick-expansion:

On Unix and a few other systems you can also use backticks for the file name
argument, for example:
    :next `find . -name ver\\*.c -print`
    :view `ls -t *.patch  \| head -n1`
The backslashes before the star are required to prevent the shell from
expanding "ver*.c" prior to execution of the find program.  The backslash
before the shell pipe symbol "|" prevents Vim from parsing it as command
termination.

Po wpisaniu polecenia pobranego z pomocy pojawia się błąd:

:next `find . -name ver\\*.c -print
E79: Cannot expand wildcards  

Dlaczego przykład z pomocy używa dwóch odwrotnych ukośników zamiast jednego i dlaczego nie działa?

Następująca praca:

  • Jeśli usunę jeden z dwóch ukośników odwrotnych, które chronią gwiazdę przed rozszerzeniem przez powłokę przed findprogramem:

    :next `find . -name ver\*.c -print`
    
  • Jeśli usunę oba ukośniki odwrotne i wstawię pojedyncze cudzysłowy wokół wzoru ver*.c:

    :next `find . -name 'ver*.c' -print`
    

Do tego momentu wydaje się, że reguła jest następująca:
jeśli polecenie powłoki zawiera gwiazdę i nie chcesz, aby powłoka rozwijała ją przed poleceniem, umieść przed nią jeden ukośnik odwrotny lub umieść pojedyncze cudzysłowy wokół wzoru .

Ale pomoc daje inny przykład:

:view `ls -t *.patch  \| head -n1`

To polecenie działa bez żadnych modyfikacji, bez potrzeby stosowania pojedynczych cudzysłowów, bez odwrotnego ukośnika.
Przypuszczam, że powodem tego jest to, że lspolecenie (w przeciwieństwie do -nameargumentu findpolecenia) akceptuje wiele argumentów pliku i nie widzi problemu z rozszerzaniem powłoki *.patch.

Teraz załóżmy, że chcę patrzeć na wszystkich plikach z rozszerzeniem .confwewnątrz /etcfolderu i rur wyjście findaby grepdostać się jedynie mecze zawierające ciąg input.
W powłoce z dowolnego katalogu roboczego wpisałbym:

find /etc -name '*.conf' | grep input

I to by działało.

W vimie napiszę to samo polecenie, umieszczając wokół niego kreski wsteczne, oraz odwrotny ukośnik przed symbolem potoku, aby uniemożliwić vimowi interpretowanie go jako zakończenia polecenia:

:next `find /etc -name '*.conf' \| grep input`

I to działa.

Teraz, jeśli wpisuję to samo polecenie bez potoku i grep input, otrzymuję błąd:

:next `find /etc -name '*.conf'`
E79: Cannot expand wildcards

Dlaczego w tym przypadku występuje błąd, mimo że chroniłem gwiazdę pojedynczymi cudzysłowami?
I dlaczego teraz jest błąd, ale nie tylko wcześniej z rurą i grep input?

Aby spróbować zrozumieć, opracowałem prostsze polecenie:

find . -name '*.conf'

Wyszukuje wszystkie pliki z rozszerzeniem .confw katalogu roboczym. Polecenie działa w powłoce.

Aby przetestować to w vimie, napisałem: :next `find . -name '*.conf'`
I działa. W tym przypadku kropka oznacza mój bieżący katalog roboczy, wyświetlany przez polecenie Ex, :pwdktóre jest moim katalogiem domowym, /home/usernameponieważ uruchomiłem z niego sesję vim.

Dlaczego to działa, gdy pytam o wyszukiwanie w bieżącym katalogu roboczym, a nie działa, gdy pytam o wyszukiwanie w dowolnym folderze, takim jak /etc?

Teraz, jeśli mogę zmienić katalog roboczy od /home/usernamedo /etcz poleceniem vim Ex :cd /etci powtórz to samo polecenie, jak poprzednio, znowu się błędy:

:next `find . -name '*.conf'`
E79: Cannot expand wildcards

Dlaczego to samo polecenie działa, gdy jestem w folderze domowym, ale nie, gdy jestem w folderze /etc?

Jestem pewien, że jest jakaś logika, ale nie mogę jej znaleźć.

Jaka jest ogólna i poprawna składnia wypełniania arglisty za pomocą dowolnego polecenia powłoki (zawierającego gwiazdę, potok, przeszukującego dowolny katalog z dowolnego katalogu roboczego)?

Używam vima w wersji 7.4.942, a zsh jest moją domyślną powłoką. Testowałem te polecenia przy minimum inicjalizacji ( vim -u NORC -N) zarówno z bash, jak i zsh.

Czy muszę skonfigurować vima, aby wywoływał bash, a nie Zsh?

saginaw
źródło
Zastanawiam się, czy próbowałeś uruchomić vima z tymi wszystkimi plikami przekazanymi jako argumenty? Mam na myśli coś takiego:vim $(find . -name ver*.c)
Vlad GURDIGA
@VladGURDIGA Właśnie próbowałem i wszystkie działają zgodnie z oczekiwaniami z powłoki: vim $(find . -name ver\*.c -print), vim $(ls -t *.patch | head -n1), vim $(find /etc -name '*.conf' | grep input), vim $(find /etc -name '*.conf'),vim $(find . -name '*.conf')
Saginaw
@VladGURDIGA Ale chciałbym wiedzieć, jak wypełnić arglistę bez wychodzenia z bieżącej sesji, ponieważ zwykle mam tylko jedną. Mógłbym również poruszać się w powłoce, wyszukiwać grupy plików i wysyłać je na serwer Vima (z argumentem --remotepatrz: vi.stackexchange.com/a/5618/4939 ), ale jestem ciekawy, jak to zrobić zrób to bezpośrednio z bieżącej sesji. Jeśli nie jest to możliwe, to w porządku, ale po przeczytaniu pomocy wydaje się, że można to zrobić. Jeśli tak, chciałbym zrozumieć, dlaczego vim tak różnie reaguje na podobne polecenia.
saginaw
Hej, wygląda na to, że w pierwszym przykładowym poleceniu brakuje ci tylnego ukośnika. ;) Poza tym, przypuszczam, że przykład z pomocy używa dwóch odwrotnych ukośników zamiast jednego, aby zapobiec rozszerzeniu powłoki, jak wyjaśniono tutaj: unix.stackexchange.com/q/104260/23669 .
Vlad GURDIGA,
W przypadku find /etc -name '*.conf'niedziałania, pomyślałbym, że niektóre śmieszne nazwy pochodzą z tego polecenia, i może to być powód, dla którego działało po przepuszczeniu grep.
Vlad GURDIGA,

Odpowiedzi:

6

Nadal nie wiem, jak użyć rozszerzenia wstecznego do wypełnienia arglisty arbitralnym poleceniem powłoki, jednak znalazłem obejście.

Od :help `=:

You can have the backticks expanded as a Vim expression, instead of as an
external command, by putting an equal sign right after the first backtick,
e.g.:
    :e `=tempname()`
The expression can contain just about anything, thus this can also be used to
avoid the special meaning of '"', '|', '%' and '#'.  However, 'wildignore'
does apply like to other wildcards.

Zamiast bezpośrednio rozszerzać polecenie powłoki, takie jak to:

:args `shell command`

Możemy wysłać polecenie powłoki do funkcji Vima systemlist()i użyć rejestru wyrażeń, =aby rozwinąć wynikowe wyrażenie w następujący sposób:

:args `=systemlist("shell command")`

Aby zapisać niektóre naciśnięcia klawiszy, zdefiniowałem następującą komendę Ex w moim vimrc ( :PAdla Populate Arglist):

command! -nargs=1 PA args `=systemlist(<q-args>)`

I przetestowałem go za pomocą różnych poleceń powłoki:

:PA find . -name ver\*.c -print 2>/dev/null
:PA find . -name 'ver*.c' -print 2>/dev/null
:PA ls -t *.patch  | head -n1
:PA find /etc -name '*.conf' 2>/dev/null | grep input
:PA find /etc -name '*.conf' 2>/dev/null
:PA find . -name '*.conf' 2>/dev/null

Do tej pory działa zgodnie z oczekiwaniami, a polecenie można wpisać tak, jak byłoby w powłoce (na przykład nie trzeba uciekać z potoku).
Wydaje się bardziej spójny i niezawodny niż bezpośrednia ekspansja wsteczna.

saginaw
źródło