Bagaimana cara menggunakan backtick-ekspansi untuk mengisi daftar argumen?

11

Dari bantuan :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.

Jika saya mengetik perintah yang diambil dari bantuan, saya mendapatkan kesalahan:

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

Mengapa contoh dari bantuan menggunakan dua garis miring terbalik bukan satu, dan mengapa tidak berhasil?

Pekerjaan berikut:

  • Jika saya menghapus salah satu dari dua garis miring terbalik yang melindungi bintang agar tidak diperluas oleh shell sebelum findprogram:

    :next `find . -name ver\*.c -print`
    
  • Jika saya menghapus kedua garis miring terbalik dan menempatkan tanda kutip tunggal di sekitar pola ver*.c:

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

Hingga saat ini, aturannya adalah:
jika perintah shell Anda berisi bintang dan Anda tidak ingin shell memperluasnya sebelum perintah, letakkan satu garis miring terbalik di depannya atau letakkan tanda kutip tunggal di sekitar pola .

Tetapi bantuan itu memberi contoh lain:

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

Perintah ini berfungsi tanpa modifikasi, tidak perlu tanda kutip tunggal, tidak perlu backslash.
Saya kira alasan mengapa ia bekerja adalah karena lsperintah (bertentangan dengan -nameargumen dari findperintah) menerima beberapa argumen file dan tidak melihat masalah dengan shell yang mengembang *.patch.

Sekarang, katakanlah saya ingin mencari semua file dengan ekstensi .confdi dalam /etcfolder dan pipa output dari finduntuk grepmendapatkan hanya pertandingan yang mengandung string input.
Dalam shell, dari direktori kerja mana pun, saya akan mengetik:

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

Dan itu akan berhasil.

Dalam vim, saya akan mengetik perintah yang sama dengan menempatkan backtick di sekitarnya, dan backslash di depan simbol pipa untuk mencegah vim menafsirkannya sebagai terminasi perintah:

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

Dan itu berhasil.

Sekarang, jika saya mengetik perintah yang sama tanpa pipa dan grep input, saya mendapatkan kesalahan:

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

Mengapa ada kesalahan dalam hal ini meskipun saya melindungi bintang dengan tanda kutip tunggal?
Dan mengapa ada kesalahan sekarang, tetapi tidak sebelum dengan pipa dan grep input?

Untuk mencoba memahami, saya telah datang dengan perintah yang lebih sederhana:

find . -name '*.conf'

Itu mencari semua file dengan ekstensi .confdi direktori kerja. Perintah itu bekerja di shell.

Untuk mengujinya di vim, saya mengetik: :next `find . -name '*.conf'`
Dan itu berhasil. Dalam hal ini titik adalah singkatan dari direktori kerja saya saat ini seperti yang ditampilkan oleh perintah Ex :pwdyang merupakan direktori home saya /home/usernamesejak saya meluncurkan sesi vim darinya.

Mengapa itu berfungsi ketika saya meminta untuk mencari di direktori kerja saat ini sedangkan itu tidak berfungsi ketika saya meminta untuk mencari di folder yang sewenang-wenang seperti /etc?

Sekarang, jika saya mengubah direktori kerja saya dari /home/usernameke /etcdengan perintah vim Ex :cd /etcdan coba lagi perintah yang sama seperti sebelumnya, sekali lagi kesalahan keluar:

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

Mengapa perintah yang sama berfungsi ketika saya berada di folder rumah saya tetapi tidak ketika saya berada di /etc?

Saya yakin ada beberapa logika, tetapi saya tidak dapat menemukannya.

Apa sintaksis umum dan yang benar untuk mengisi arglist dengan perintah shell arbitrary (berisi bintang, pipa, mencari di direktori mana saja dari direktori kerja)?

Saya menggunakan vim versi 7.4.942 dan zsh adalah shell default saya. Saya menguji perintah ini dengan inisialisasi minimum ( vim -u NORC -N) dari bash dan juga dari zsh.

Apakah saya perlu mengkonfigurasi vim untuk memanggil bash dan bukan zsh?

saginaw
sumber
Saya ingin tahu apakah Anda mencoba memulai vim dengan semua file yang dilewatkan sebagai argumen? Maksud saya sesuatu seperti:vim $(find . -name ver*.c)
Vlad GURDIGA
@VladGURDIGA Saya baru saja mencoba dan mereka semua bekerja seperti yang diharapkan dari shell: 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 Tapi saya ingin tahu bagaimana cara mengisi argumen tanpa keluar dari sesi saat ini karena saya biasanya hanya satu. Saya juga bisa bergerak di shell, mencari sekelompok file dan mengirimnya ke server Vim (dengan argumen --remote, lihat: vi.stackexchange.com/a/5618/4939 ) tapi saya ingin tahu bagaimana caranya lakukan langsung dari sesi saat ini. Jika tidak mungkin itu baik-baik saja, tetapi setelah membaca bantuan itu tampaknya bisa dilakukan. Jika ya, saya ingin memahami mengapa vim bereaksi sangat berbeda terhadap perintah serupa.
saginaw
Hei, sepertinya pada perintah sampel pertama Anda melewatkan backtick penutup. ;) Selain itu, saya kira contoh dari bantuan menggunakan dua backslash bukan satu untuk mencegah ekspansi shell, seperti yang dijelaskan di sini: unix.stackexchange.com/q/104260/23669 .
Vlad GURDIGA
Dalam hal find /etc -name '*.conf'tidak bekerja, saya pikir beberapa nama lucu keluar dari perintah itu, dan, ini mungkin menjadi alasan mengapa itu berhasil ketika disalurkan grep.
Vlad GURDIGA

Jawaban:

6

Saya masih tidak tahu bagaimana menggunakan backtick-expansion untuk mengisi argumen dengan perintah shell arbitrer, namun saya telah menemukan solusinya.

Dari :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.

Jadi, alih-alih memperluas secara langsung perintah shell seperti ini:

:args `shell command`

Kami dapat mengirim perintah shell ke fungsi Vim systemlist()dan menggunakan register ekspresi =untuk memperluas ekspresi yang dihasilkan seperti ini:

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

Untuk menyimpan beberapa penekanan tombol, saya telah mendefinisikan perintah Ex berikut di vimrc saya ( :PAuntuk Populate Arglist):

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

Dan mengujinya dengan berbagai perintah shell:

: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

Sejauh ini berfungsi seperti yang diharapkan dan perintah dapat diketik seperti di shell (tidak perlu keluar dari pipa misalnya).
Tampaknya lebih konsisten dan dapat diandalkan daripada ekspansi backtick langsung.

saginaw
sumber