Quando si invoca vim
attraverso find | xargs
, in questo modo:
find . -name "*.txt" | xargs vim
ricevi un avviso in merito
Input is not from a terminal
e un terminale con un comportamento praticamente rotto in seguito. Perché?
Quando si invoca vim
attraverso find | xargs
, in questo modo:
find . -name "*.txt" | xargs vim
ricevi un avviso in merito
Input is not from a terminal
e un terminale con un comportamento praticamente rotto in seguito. Perché?
Risposte:
Quando si richiama un programma tramite xargs
, lo stdin (input standard) del programma punta a /dev/null
. (Dato che xargs non conosce lo stdin originale , fa la cosa migliore dopo.)
$ true | xargs filan -s 0 chrdev / dev / null 1 tty / dev / pts / 1 2 tty / dev / pts / 1 $ true | xargs ls -l / dev / fd /
Vim si aspetta che il suo stdin sia uguale al suo terminale di controllo ed esegue direttamente vari ioctl relativi al terminale su stdin. Quando fatto su /dev/null
(o qualsiasi descrittore di file non tty), questi ioctl sono privi di significato e restituiscono ENOTTY, che viene silenziosamente ignorato.
La mia ipotesi su una causa più specifica: all'avvio Vim legge e ricorda le vecchie impostazioni del terminale e le ripristina all'uscita. Nella nostra situazione, quando vengono richieste le "vecchie impostazioni" per un non tty fd (descrittore di file), Vim riceve tutti i valori vuoti e tutte le opzioni disabilitate e imposta incurantemente le stesse sul terminale.
Puoi vederlo eseguendo vim < /dev/null
, uscendo, quindi eseguendo stty
, che genererà un sacco di <undef>
s. Su Linux, l'esecuzione stty sane
renderà nuovamente utilizzabile il terminale (anche se avrà perso opzioni come iutf8
, probabilmente causando fastidi minori in seguito).
Potresti considerarlo un bug in Vim, dal momento che può aprirsi /dev/tty
per il controllo terminale, ma non lo fa. (Ad un certo punto durante l'avvio, Vim duplica il suo stderr su stdin, il che gli consente di leggere i comandi di input - da un fd aperto per la scrittura - ma anche questo non è fatto abbastanza presto.)
stty sane
reset
invece di stty sane
e funziona bene dopo.
(A seguito di spiegazione di grawity, che xargs
punta stdin
a /dev/null
.)
La soluzione per questo problema è aggiungere il -o
parametro a xargs
. Da man xargs
:
-o
Riaprire stdin come
/dev/tty
nel processo figlio prima di eseguire il comando. Ciò è utile se si desideraxargs
eseguire un'applicazione interattiva.
Pertanto, la seguente riga di codice dovrebbe funzionare per te:
trova . -name "* .txt" | xargs -o vim
GNU xargs supporta questa estensione da qualche versione nel 2017 (con il nome dell'opzione lunga --open-tty
).
Per le versioni precedenti o di altre versioni di xargs, è possibile passare esplicitamente /dev/tty
per risolvere il problema:
find . -name "*.txt" | xargs bash -c '</dev/tty vim "$@"' ignoreme
(Il file ignoreme
è lì per richiedere $ 0, in modo che $ @ sia tutti gli argomenti di xargs.)
$@
non sembra tradurre correttamente gli argomenti.
function vimin () { xargs sh -c 'vim "$@" < /dev/tty' vim; }
ignoreme
stringa fittizia , vedi vi.stackexchange.com/a/17813
The -J, -o, -P and -R options are non-standard FreeBSD extensions which may not be available on other operating systems.
(Non era disponibile su macOS per me perché ho installato xargs da homebrew (quello GNU))
La via più facile:
vim $(find . -name "*foo*")
Dovrebbe funzionare bene se usi l'opzione -exec su find piuttosto che eseguire il piping in xargs.
find . -type f -name filename.txt -exec vi {} +
+
(invece del "solito" \;
) per mettere tutti i file trovati in una sessione di Vim - un'opzione di cui continuo a dimenticare. Hai ragione, ovviamente, e +1 per quello. Uso vim $(find ...)
semplicemente per abitudine. Tuttavia, in realtà stavo chiedendo perché l'operazione di tubatura rovina il terminale, e la gravità lo ha inchiodato con la sua spiegazione.
Usa invece GNU Parallel:
find . -name "*.txt" | parallel -j1 --tty vim
O se vuoi aprire tutti i file in una volta sola:
find . -name "*.txt" | parallel -Xj1 --tty vim
Tratta anche correttamente con nomi di file come:
My brother's 12" records.txt
Guarda il video introduttivo per saperne di più: http://www.youtube.com/watch?v=OpaiGYxkSuQ
vim $(find . -name "*.txt")
è più semplice e tutti i file vengono aperti contemporaneamente.
find | xargs
e $(find)
avranno grossi problemi con gli spazi nei nomi dei file.
$IFS
, -print0
e roba del genere, e poi hai lasciato il regno di una soluzione a riga di comando one-shot e hai raggiunto un punto in cui dovresti trovare uno script ... c'è un motivo per cui gli spazi nei nomi dei file sono scoraggiati .
forse non il migliore ma qui lo script che uso (chiamato vim-open
):
#!/usr/bin/env ruby
require 'shellwords'
inputs = (ARGV + (STDIN.tty? ? [] : STDIN.to_a)).map(&:strip)
exec("</dev/tty vim #{inputs.flatten.shelljoin}")
funzionerà con vim-open a b c
e ls | vim-open
per esempio
find
oxargs
affatto. Apri vim senza argomenti, quindi esegui:args **/*.txt<CR>
per impostare gli argomenti di vim dall'interno dell'editor.