Cara memasukkan string secara otomatis setelah konfirmasi

2

Saya memiliki bashjendela terminal tempat saya mengeksekusi satu perintah secara eksklusiffoo . Karena itu saya ingin setiap baris (setelah prompt tentu saja) dimulai dengan "foo" sehingga saya hanya perlu mengetikkan opsi dan argumen fungsi, tetapi bukan nama fungsi berulang. Tentu saja akan menyenangkan untuk dapat mengubah string yang dimasukkan secara otomatis juga, tetapi itu tidak penting bagi saya.

Contoh

Ketika saya membuka terminal, tanpa mengetik apa pun yang ingin saya lihat adalah:

user@host:~/ $ foo 

Kemudian saya mengetik --option argumentdan ketika saya menekan Enterfungsi foodipanggil dengan yang diberikan --optiondan argument.

Apa yang saya coba

Saya mencoba untuk bermain-main dengan $PS1dan $PROMPT_COMMANDmenggunakan xdotool type "foo "dan mengherankan yang benar-benar berfungsi, tetapi sayangnya itu juga mencetak "foo" sebelum prompt, yang cukup jelek:

user@host:~/ $ PROMPT_COMMAND='xdotool type "foo "'
foo user@host:~/ $ foo 

Saya juga menemukan dan mencoba preexecfungsi dari script bash-preexec Ryan Caloras , tetapi memiliki masalah yang persis sama.

Bagaimana cara menggaungkan (a (executable-) string) ke prompt, sehingga kursor berkedip di akhir baris? terkait, tetapi jawaban di sana tidak memungkinkan untuk menambahkan sesuatu ( --option argument) ke perintah yang akan dieksekusi. Tapi saya tidak menguji zsh- harus ada bashsolusi untuk hal sederhana seperti itu, bukan begitu?

pencuci mulut
sumber
Jika berhasil, tentu, silakan.
muru
@uru saya diuji expect, tetapi tidak memasukkan teks pada setiap prompt, tetapi hanya pada yang pertama. Sementara itu ingin di pertanyaan terkait, tujuan saya berbeda.
hidangan penutup

Jawaban:

5

Dengan zsh, seharusnya hanya masalah:

precmd() print -z 'foo '

Atau untuk menghindarinya mengesampingkan perintah yang antri oleh pengguna dengan Alt+q:

zle-line-init() { [ -n "$BUFFER" ] || LBUFFER='foo '; }
zle -N zle-line-init

Dengan bash, alih-alih xdotool, Anda dapat menggunakan TIOCSTI ioctl:

insert() {
  perl -le 'require "sys/ioctl.ph";
            ioctl(STDIN, &TIOCSTI, $_) for split "", join " ", @ARGV' -- "$@"
}
PROMPT_COMMAND='insert "foo "'

Lebih disukai xdotoolkarena menyisipkan karakter-karakter tersebut secara langsung ke buffer input perangkat bash. xdotoolhanya akan berfungsi jika ada server X yang berjalan (tidak akan berfungsi pada konsol atau terminal nyata atau lebih ssh(tanpa -X) misalnya), bahwa itu adalah yang diidentifikasi oleh $DISPLAYdan itulah yang Anda berinteraksi dengan, dan bahwa terminal emulator yang bashsedang berjalan memiliki fokus ketika $PROMPT_COMMANDdievaluasi.

Sekarang, seperti pada Anda xdotoolhal, karena ioctl()dilakukan sebelum prompt ditampilkan dan tty garis terminal disiplin menempatkan keluar dari icanon+echomodus dengan readline, Anda cenderung melihat gema dari yang foooleh disiplin baris tty mengacaukan layar.

Anda dapat mengatasinya dengan memasukkan string yang gema- nya tidak terlihat (seperti U + 200B jika menggunakan lokal Unicode secara eksklusif) dan mengikatnya ke tindakan yang memasukkan "foo ":

insert() {
  perl -le 'require "sys/ioctl.ph";
            ioctl(STDIN, &TIOCSTI, $_) for split "", join " ", @ARGV' -- "$@"
}
bind $'"\u200b":"foo "'
PROMPT_COMMAND="insert $'\u200b'"

Atau Anda dapat menunda TIOCSTIcukup ioctl agar readline punya waktu untuk menginisialisasi:

insert_with_delay() {
  perl -le 'require "sys/ioctl.ph";
            $delay = shift @ARGV;
            unless(fork) {
              select undef, undef, undef, $delay;
              ioctl(STDIN, &TIOCSTI, $_) for split "", join " ", @ARGV;
            }' -- "$@";
}
PROMPT_COMMAND='insert_with_delay 0.05 "foo "'

Jika, seperti dalam zshpendekatan, Anda ingin menangani kasus di mana pengguna memasukkan teks sebelum prompt ditampilkan, Anda bisa melakukannya

  • tiriskan itu dengan melakukan tcflush(0, TCIFLUSH)in perlsebelum TIOCSTIioctl (juga perlu -MPOSIXopsi), atau
  • seperti dalam zshpendekatan, pastikan foodimasukkan pada awal buffer dengan memasukkan ^A(dengan asumsi Anda menggunakan emacsmode pengeditan (default) di mana yang memindahkan kursor ke awal baris) sebelum foodan ^Esesudah (untuk pindah ke akhir) :

    insert_with_delay 0.05 $'\1foo \5'

    atau

    bind $'"\u200b":"\1foo \5"'
Stéphane Chazelas
sumber
The insertFungsi membutuhkan sedikit pekerjaan untuk benar mengutip argumen yang memerlukan mengutip. Mungkin denganperl ... -- "$(printf "%q " "$@")"
glenn jackman
Hai @glenn. Tidak yakin apa yang kamu maksud. Di sini kami ingin memasukkan foo atau karakter U + 200B, bukan'foo '
Stéphane Chazelas
Mungkin aku terlalu memperumit masalah. Saya sedang memikirkan kasus di mana teks pra-populasi akan seperti di foo "bar baz" mana argumen ke-2 perlu dikutip.
glenn jackman
@ Glennjackman, saya akan mengatakan bahwa harus diurus oleh penelepon. insertdi sini dimaksudkan untuk mendapatkan kode shell sebagai argumen (seperti eval) sehingga Anda dapat menyebutnya dengan insert 'VAR="x y" cmd2 'atau insert 'cmd "${default_opt[@]}" 'misalnya yang tidak dapat Anda lakukan jika insertmengutip argumennya sendiri.
Stéphane Chazelas
Saya menggunakan pendekatan Anda yang mencengangkan dalam Ajukan pertanyaan Ubuntu ini, di mana masalah awalnya muncul: Hanya Menampilkan Perintah Utama Terminal Jendela
makanan penutup
3

Apa yang Anda minta tidak terdengar biasa sehingga tidak mengherankan bahwa tidak ada cara langsung untuk melakukannya.

Dengan bash murni, Anda bisa menggunakan readbuiltin untuk membaca baris, lalu evaluntuk mengeksekusinya. Simulasikan bisikan (Saya mendekati bisikan Anda di bawah).

#!/bin/bash    
PROMPT=$'\e[1m'$USER@$HOSTNAME:$PWD$' $ \e[0m'
IFS= read -r -e -i "foo " -p "$PROMPT" line
eval "$line"
exit

Masukkan ini ke dalam skrip dan beri tahu emulator terminal Anda untuk menjalankan skrip itu.

Atau, jalankan Screen di terminal emulator. Anda dapat menggunakan stuffperintahnya untuk mensimulasikan input.

Gilles
sumber