xargs ve vi - “Giriş bir terminalden değil”


14

php.iniSistemimde her yerde bulunan yaklaşık 10 dosyam var ve bunlara hızlıca göz atmak istedim. Bu komutu denedim:

locate php.ini | xargs vi

Ama vibeni uyarıyor Input is not from a terminalve sonra konsol gerçekten garip olmaya başlıyor - bundan sonra ssh oturumundan :q!çıkmak vive konsolun tekrar normal davranması için yeniden bağlanmak için basmam gerekiyor.

Burada neler olduğunu anladığımı düşünüyorum - temel olarak komut vibaşlatıldığında bitmedi, bu yüzden komut belki de bitmedi ve viterminalin normal modda olduğunu düşünmüyor.

Nasıl düzeltebileceğimi bilmiyorum. Google'da ve unix.stackexchange.com'da kötü şanslarla arama yaptım.



Bir yan not olarak, resetterminalinizi vidalandığında sıfırlamak için çalışabilirsiniz (ssh oturumuyla bağlantıyı kesmeniz gerekmez).
wisbucky

Yanıtlar:


12
vi $(locate php.ini)

Not: dosya yollarınızda boşluk varsa bununla ilgili sorunlar olacaktır, ancak işlevsel olarak komutunuza eşdeğerdir.
Bu sonraki sürüm boşlukları düzgün bir şekilde işleyecek, ancak biraz daha karmaşık olacak (dosya adlarındaki yeni satırlar yine de kıracak)

(IFS=$'\n'; vi $(locate php.ini))


Açıklama:

Olanlar, programların dosya tanımlayıcılarını, onları ortaya çıkaran süreçten miras almasıdır. xargsait STDOUT bağlı olan STDIN vardır locate, böylece vihiçbir ipucu gerçekten ne özgün STDIN sahiptir.


2
xargs harika, en sevdiğim araçlardan biri - veri beslemesi dışında herhangi bir şey için stdin kullanan programlarla kullanım için uygun değil. Cevabınızı ve bunun dışındaki açıklamalarınızı beğendim, bu yüzden +1 zaten :)
cas

@CraigSanders Sevmiyorum çünkü kötüye kullanımı (yanlış kullanın) ve sonunda kırılması çok kolay. Kesinlikle kullanmak zorunda olduğum hiçbir şeyle karşılaşmadım, bunun xargsiçin doğrudan kabuk (veya find) ile yapılamazdı . Ancak bunun en iyi çözüm olacağı durumları düşünebilirim. Öyleyse, ne xargsyaptığını, argümanları nasıl böldüğünü, programı nasıl çalıştırdığını vb. Ve düzgün bir şekilde kullandığınızı anladığınız sürece, şunu söyleyebilirim :-P
Patrick

... | awk '{print $3}' | xargs | sed -e 's/ /+/g' | bc(alan 3'ün tüm değerlerini toplamak için) gibi şeyler için atılamaz . veya sed -e 's/ /|/g'bir regexp oluşturmak için. ve evet, herhangi bir araç gibi, onu nasıl kullanacağınızı ve sınırlarının ve uyarılarının ne olduğunu bilmeniz gerekir.
cas

vi $(...)Yaklaşım aynı zamanda başka kabuklarla simgelemlerin ile ilgili bir sorun vardır zsh.
Stéphane Chazelas

Ayrıca ile dikkat xargsboşluk sorunu yanında yaklaşımla, tek tırnak çift tırnak ve ters eğik çizgi ile dosya adları da sorun vardır.
Stéphane Chazelas

10

Bu soru daha önce Süper Kullanıcı forumunda sorulmuştu .

@ Grawity'nin bu soruya verdiği yanıttan alıntı:

Bir programı xargs aracılığıyla çağırdığınızda, programın stdin (standart giriş) / dev / null öğesini gösterir. (Xargs orijinal stdin'i bilmediğinden, bir sonraki en iyi şeyi yapar.)

Vim, stdin'in kontrol terminali ile aynı olmasını bekler ve doğrudan stdin üzerinde terminalle ilgili çeşitli ioctl'ler gerçekleştirir. / Dev / null (veya herhangi bir tty olmayan dosya tanımlayıcı) üzerinde yapıldığında, bu ioctls anlamsızdır ve sessizce yok sayılan ENOTTY değerini döndürür.

Bu, xarg kılavuz sayfalarında belirtilmiştir. OSX / BSD'den:

-o Komutu çalıştırmadan önce alt işlemi / dev / tty olarak yeniden açın. Bu, xargs'ın etkileşimli bir uygulama çalıştırmasını istiyorsanız kullanışlıdır.

Bu nedenle, OSX'te aşağıdaki komutu kullanabilirsiniz:

find . -name "php.ini" | xargs -o vim

GNU sürümünde doğrudan bir anahtar olmasa da, bu komut çalışacaktır. (Dizeyi eklediğinizden emin olun dummy, aksi takdirde ilk dosyayı bırakır.)

find . -name "php.ini" | xargs bash -c '</dev/tty vim "$@"' dummy

Yukarıdaki çözümler SuperUser'da Jaime McGuigan'ın izniyle kullanılmıştır . Sitede bu hatayla ilgili arama yapan gelecekteki ziyaretçiler için buraya ekleniyor.


3
-O ipucu için +1 teşekkürler. Ben yıllardır xargs kullanıyorum ve asla fark ettim .... sadece bir GNU xargs özelliği olmadığı için benim sistemindeki man sayfasını kontrol etti. Man sayfası, xargs sh -c 'emacs "$@" < /dev/tty' emacsdaha esnek ve taşınabilir bir seçenek olduğunu iddia ettikleri gibi sağlar (GNU'nun özelliklere taşınabilirliği tercih etmesi biraz komik olsa da :).
cas

2

GNU findutilsve işlem ikamesi için destekli bir kabuk (ksh, zsh, bash) ile şunları yapabilirsiniz:

xargs -r0a <(locate -0 php.ini) vi

Fikir -a filenamestdin yerine dosya listesini geçmek . Kullanmak -0, dosya adlarının içerebileceği karakterlerden veya karakter olmayan karakterlerden bağımsız olarak çalışmasını sağlar.

İle zshşunları yapabilirsiniz:

vi ${(0)"$(locate -0 php.ini)"}

( 0NUL'lara bölünecek parametre genişletme bayrağı nerede ).

Ancak, bunun tersine, hiçbir dosya bulunmazsa xargs -rhala viargüman olmadan çalıştığını unutmayın .


0

Aynı düzenleyicide birden fazla php.ini düzenlensin mi?

Deneyin: vim -o $(locate php.ini)


0

Bu hata, vim çağrıldığında ve terminal yerine önceki boru hattının çıkışına bağlandığında ve farklı beklenmedik girişler (NUL'ler gibi) aldığında oluşur. Çalıştırdığınızda aynı olur: vim < /dev/nullyani resetbu durumda komut yardımcı olur. Bu, süper kullanıcıdaki yerçekimi ile açıklanmaktadır .

Unix / OSX üzerinde kullanabilirsiniz xargsile -o, parametre gibi:

locate php.ini | xargs -o vim

-oKomutu çalıştırmadan önce stdin'i alt süreçte / dev / tty olarak yeniden açın. Bu, xargs'ın etkileşimli bir uygulama çalıştırmasını istiyorsanız kullanışlıdır.

Linux'ta aşağıdaki geçici çözümü deneyin:

locate php.ini | xargs -J% sh -c 'vim < /dev/tty $@'

Alternatif olarak , tty tahsisini zorlamak parallelyerine GNU kullanın, xargsörneğin:

locate php.ini | parallel -X --tty vi

Not: parallelUnix / OSX'te farklı parametreleri olduğundan ve tty'yi desteklemediğinden çalışmaz.

İyi (gibi birçok diğer popüler komutlar sözde tty tahsisini sağlayan -tiçinde ssh), bu yüzden yardım edin.

Alternatif olarak find, düzenlemek üzere dosya adlarını iletmek için kullanın ; bu nedenle xargs, yalnızca aşağıdakileri kullanın -exec:

find /etc -name php.ini -exec vim {} +

0

@ Patrick'in IFSkesmek sadece bashve gibi aptal mermiler için gereklidir zsh. fish dizeyi varsayılan olarak yeni satırlara böler.

$ vim (locate php.ini)

Ve eğer tek birimizin aslında adında yeni satır içeren bir dosya varsa, Tanrı hepimize yardım eder. Linux'u 17 yıl kullandıktan sonra bir kez bile görmedim. Sadece ne olursa olsun çalışması gereken komut dosyaları için dosya adlarında yeni satırlarla desteklemeyi rahatsız ederdim, ancak böyle komut dosyaları muhtemelen vim etkileşimli olarak çalışmıyor.


zshvarsayılan olarak SPC, SEKME, NL ve NUL üzerinde böler. Karşılaştırmadığı şey bash, sonuçta globbing yapmaktır, bu nedenle dosya adlarındaki joker karakterler bir sorun değildir. İçinde zsh, açık bir bölme operatörü için cevabımda IFS=$'\0'; vi $(locate -0 php.ini)gösterdiğim gibi ya da yaptığım gibi vi ${(0)"$(locate -0 php.ini)"}. Ayrıca tcsh's unutmayınvi "`locate php.ini`"
Stéphane Chazelas

aw, saçmalık. Tamam bu işe yarıyor: $ f='not there'<ret>$ ls $f<ret>ama bu çalışmıyor: ls echo not there. Tamam, bunu biraz güncellemem gerekiyor gibi görünüyor.
enigmaticPhysicist

Evet, zsh bunu yaparken doğru olanı yapmaz ls "$(echo test; echo other test)". Sadece balık doğru olanı yapar.
enigmaticPhysicist

Tırnaklar olmadan aynı şeyi kastettiğinizi varsayarsak, bu "doğru" değil, çizgiler üzerinde bölünüyor, sadece farklı bir seçim. zsh varsayılan olarak kelimelere ayrılır (diğer tüm kabuklar gibi) ve muğlak $IFSişleçler ( fve 0parametre genişletme bayrakları) üzerinden veya aracılığıyla satırlara veya NUL'lara bölünmesi söylenebilir . Rasgele dosya adları için, kelimeye göre bölme veya satıra göre bölme eşit derecede yanlışsa , NUL'a bölmeniz veya fishyapamayan bazı kodlamaları ayrıştırmanız gerekir . İçinde zsh, IFS=$'\0'; ls -ld -- $(printf '%s\0' "$file1" "$file2")ya dals -ld -- ${(0)"$(printf '%s\0' "$file1" "$file2")"}
Stéphane Chazelas

Meh. Yeni satırlara bölmek yeterince iyi. Yanıtın dediği gibi, dosya adlarındaki yeni satırlar oldukça nadirdir. Tam anlamıyla 17 yıl içinde olduğunu hiç görmedim. Ve yeni satırlar nullerden çok daha uygun ayırıcılar.
enigmaticPhysicist

0

Hızlı bir yolu SPC, TAB, NL içeren dosya yolları hiçbiri garanti edemez varsayarak, bunu yapmak için *, ?, [(aynı zamanda karakterleri \ve {...}bazı kabuklarda) arka kene kullanmaktır (aka vahim aksan) önce bir komut çalıştırmak için çalışan başka bir komut.

Örneğin

vi `find / -type f -name 'php.ini'`

Geri keneler içinde bulunan komut ilk önce yürütülür. Daha sonra içerilen komutun çıkışı geri kenelerden önce belirtilen komutla yürütülür.

Örneğin, yukarıdaki satırda, find / -type f -name 'php.ini'komut viönce yürütülür, çıktı gönderilir ve daha sonra bu çıktıya uygulanan split + glob sonucu yürütülür.


3
arka tırnaklar tek tırnaklar için çok kolay karıştırılır. kullanın $(find ...).
cas

1
bunun dosya adlarındaki boşluklara ve / veya satırlara da zarar vereceğini tahmin etmek?
cwd

Bash komut dosyasında kabuk komutlarını bu şekilde yürütürsünüz. Senaryolarımda veya tek bir satırda boşluklarda veya yeni satırlarda hiçbir şey kırılmadı. Ancak, vibu yöntemi kullanarak birden fazla dosya açmaya çalışmadım . viÇıktının nasıl okunduğuna ve yürütüldüğüne bağlı olarak yeni satırlara veya boşluklara zarar verebilir .
tacotuesday
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.