Vi'yi find üzerinden çağırmak | xargs terminalimi bozuyor. Neden?


137

Ne zaman çağırma vimyoluyla find | xargsbu gibi:

find . -name "*.txt" | xargs vim

hakkında uyarı almak

Input is not from a terminal

ve daha sonra kırılan davranışları olan bir terminal. Neden?


11
Yan not: Bu işlemi kullanmadan findveya hiç kullanmadan tamamen vim içinde yapabilirsiniz xargs. Argüman olmadan vim'i açın, sonra :args **/*.txt<CR>vim'in argümanlarını editörün içinden ayarlamak için çalıştırın .
Trevor Powell

3
@TrevorPowell: Bütün bu yıllar boyunca, vim beni şaşırtmaktan asla vazgeçmedi.
DevSolar



Yanıtlar:


100

Bir program aracılığıyla çağırdığınızda xargs, programın stdin (standart giriş) işaret eder /dev/null. (Xargs orijinal stdin'i tanımıyorsa, bir sonraki en iyi şeyi yapar.)

$ doğru | xargs filan -s
    0 chrdev / dev / null
    1 tty / dev / puan / 1
    2 tty / dev / puan / 1

$ doğru | xargs ls - l / dev / fd /

Vim, stdin'in kontrol terminali ile aynı olmasını bekler ve doğrudan terminal üzerinde çeşitli terminal ile ilgili ioctl'ler gerçekleştirir . Burgulu /dev/null(veya olmayan herhangi bir tty dosya tanıtıcı), bu ıoctl'ler anlamsızdır ve sessizce yok sayılır ENOTTY, geri dönün.

  • Benim daha özel bir neden olduğunu tahmin ediyorum: Açılışta Vim eski terminal ayarlarını okuyor ve hatırlıyor ve çıkarken tekrar geri yüklüyor. Bizim durumumuzda, tt olmayan bir fd (dosya tanımlayıcısı) için "eski ayarlar" istendiğinde, Vim tüm değerleri boş alır ve tüm seçenekler devre dışı bırakılır ve dikkatsizce terminalinize ayarlar.

    Bunu çalıştırarak vim < /dev/null, çıkarak, sonra çalıştırarak görebilirsiniz stty, ki bu da çok fazla <undef>s üretecektir . Linux'ta, çalıştırma stty saneterminali tekrar kullanılabilir hale getirecektir ( daha sonra muhtemelen küçük sıkıntılara neden olacak gibi seçenekleri kaybetmiş olmasına rağmen iutf8).

Bunu beri, bu Vim bir hata düşünebiliriz edebilirsiniz açmak /dev/ttyterminali kontrolü için, ama yok. (Başlangıç ​​sırasında bir noktada Vim, stderr komutunu stdin'e kopyalar, bu da giriş komutlarınızı - yazı yazmak için açılan bir fd'den - ancak yeterince erken yapılmadan okumasını sağlar.)


20
+1 ve TL için, DR çalışanları yeni çalıştırıldıstty sane
doc_id

@rahmanisback: Diğer cevaplar, artı Trevor'un yorumları, ilk başta terminalde kırılmayı önlemenin yollarını verdi. Grawity'nin cevabını kabul ettim, çünkü sorum “neden”, “nasıl kaçınılması” değildi - bu , aslında bunu ortaya çıkaran başka bir soru tarafından ele alındı .
DevSolar

@DevSolar Anlayın, ama benim gibi sadece bu davranıştan nasıl kurtulacağımı düşünen, benim gibi düşünen insanlar hakkında düşünün - ne yazık ki olmasın - şu anda çok ilginç olan "neden" i incelemek için yeterli zamanınız var.
doc_id

4
terminalim koptuğunda, resetbunun yerine bunun yerine kullanıyorum stty saneve ondan sonra düzgün çalışıyor.
Capi Etheriel

136

(Grawity açıklamasıyla, itibaren ardından xargsişaret stdinetmek /dev/null.)

Solüsyon bu sorun için eklemektir -oparametreyi xargs. Kimden man xargs:

-o

      /dev/ttyKomutu çalıştırmadan önce stdin'i alt süreçte olduğu gibi yeniden açın . xargsEtkileşimli bir uygulama çalıştırmak istiyorsanız bu yararlıdır .

Bu nedenle, aşağıdaki kod satırı sizin için çalışmalıdır:

Bul -smi "* .txt" | xargs -o vim

GNU xargs, 2017'deki bazı sürümlerden bu yana bu uzantıyı desteklemektedir (uzun seçenek adıyla --open-tty).

Xargs'ın daha eski veya diğer sürümleri /dev/ttyiçin sorunu çözmek için açıkça girebilirsiniz :

find . -name "*.txt" | xargs bash -c '</dev/tty vim "$@"' ignoreme

( ignoremeOrada $ 0 almak için orada, böylece $ @, xargs dosyasındaki tüm argümanlardır.)


2
Bundan bir bash takma adı nasıl oluşturabilirsin? $@argümanları doğru çevirmiyor gibi görünüyor.
zanegray,

1
@zanegray - takma ad yapamazsınız, ancak bunu bir işlev haline getirebilirsiniz. Deneyin:function vimin () { xargs sh -c 'vim "$@" < /dev/tty' vim; }
Christopher,

GNU xargs çözümünün nasıl çalıştığı ve neden sahte kuklaya ihtiyaç duyduğunuzun ayrıntılı bir açıklaması için ignoreme, bkz. Vi.stackexchange.com/a/17813
20

@zanegray, Takma ad yapabilirsiniz. Tırnaklar zor. Vi.stackexchange.com/a/17813
wisbucky

The -J, -o, -P and -R options are non-standard FreeBSD extensions which may not be available on other operating systems.(Benim için macOS'ta mevcut değildi, çünkü homebrew'den (GNU one) xargs yükledim)
localhostdotdev

33

En kolay yol:

vim $(find . -name "*foo*")

5
Asıl soru “neden”, “nasıl önleneceği” değildi ve iki buçuk yıl önce memnuniyetine cevaplandı.
DevSolar

5
Bu, elbette, dosya adları boşluk veya başka özel karakterler içerdiğinde düzgün çalışmaz ve ayrıca bir güvenlik riskidir.
Dejay Clayton

1
Favori cevabım, sadece "bulma" ya da joker karakterleri değil, dosyaları listeleyen her komut için işe yaradığı için. Dejay'in işaret ettiği gibi biraz güven gerektirir.
Travis Wilson

1
Bu, xargs için tasarlanan birçok kullanım durumuyla işe yaramaz: örneğin, yol sayısı çok yüksek olduğunda (cc @TravisWilson)
Good Person

21

Eğer xargs'a eklemek yerine find -exec seçeneğini kullanırsanız gayet iyi çalışması gerekir.

find . -type f -name filename.txt -exec vi {} + 

2
Ha ... hile var +(yerine "olağan" nin \;içine bulunan tüm dosyaları almak için) bir seçenek I - Vim oturumu devam unutuyor. Tabii ki haklısın ve bunun için +1. Sadece vim $(find ...)alışkanlık dışı kullanırım. Ancak, aslında boru operasyonunun neden terminale zarar verdiğini sordum ve bunun açıklamasında yerçekimini çiviledi.
DevSolar

2
Bu en iyi cevaptır ve hem BSD / OSX / GNU / Linux'ta çalışır.
kevinarpe

1
Ayrıca, eşzamanlı olarak eşzamanlı olarak düzenlenmesi gereken dosyaların bir listesini elde etmenin tek yolu da bulun. Desenli tüm dosyaları bulmak için grep'i kullanabilir ve bunları aynı anda düzenlemeyi deneyebilirim.
Chandranshu

8

Bunun yerine GNU Parallel kullanın:

find . -name "*.txt" | parallel -j1 --tty vim

Veya tüm dosyaları tek seferde açmak istiyorsanız:

find . -name "*.txt" | parallel -Xj1 --tty vim

Hatta gibi dosya isimleri ile doğru bir şekilde ilgilenir:

My brother's 12" records.txt

Daha fazla bilgi edinmek için tanıtım videosunu izleyin: http://www.youtube.com/watch?v=OpaiGYxkSuQ


1
Her yerde mevcut değil. Günün çoğunda ek araçlar yükleme yetkisi olmayan sunucular üzerinde çalışıyorum. Ama yine de ipucu için teşekkürler.
DevSolar 16:11

'Cat> dosyasını yapma özgürlüğüne sahipseniz; chmod + x dosyası 'sonra GNU Parallel yükleyebilirsiniz: Bu sadece bir perl betiğidir. Man sayfalarına ve isterseniz böyle, size homedir altında yükleyebilirsiniz: ./configure --prefix = $ ANA && make install && yapmak
Ole Tange

2
Tamam, bu çalıştı - ama paralel gelmez değil tüm dosyaları açmak, bunları açmak yapar arkaya . Aynı zamanda basit bir işlem için oldukça ağız doludur. vim $(find . -name "*.txt")daha basittir ve tüm dosyaları aynı anda açabilirsiniz.
DevSolar

5
@DevSolar: Biraz alakasız, ancak her iki find | xargsve $(find)dosya adlarında boşluklu büyük sorunlar olacaktır.
Grawity

2
@grawity Doğru, ama (bildiğim) etrafında kolay bir yolu yoktur. Sen kurcalıyor başlamak olurdu $IFS, -print0ve şeyler, o zaman bir kerelik bir komut satırı çözümün bölge sol ve bir komut dosyası hazırlaması gerektiğini bir noktaya ulaştı ... Dosya adlarında boşluklar cesaretini neden bir sebebi var .
DevSolar 21:11

0

belki de en iyisi değil ama burada kullandığım senaryo (adlandırılmış vim-open):

#!/usr/bin/env ruby

require 'shellwords'

inputs = (ARGV + (STDIN.tty? ? [] : STDIN.to_a)).map(&:strip)
exec("</dev/tty vim #{inputs.flatten.shelljoin}")

birlikte çalışacak vim-open a b cve ls | vim-openörneğin


Diğer birkaç cevaba gelince, asıl sorunun "nasıl önleneceğini" değil "neden" olduğunu unutmayın. (Bunun için hala Trevor'ın sorusuna göre yorum
yazmasını
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.