Bir komut üzerinde iki komut çalıştırın (script olmadan)


28

Bir girişi iki kez, girişi iki kez yazmadan nasıl yapabilirim?

Örneğin, stat komutu bir dosya hakkında çok şey anlatır ancak dosya türünü göstermez:

stat fileName

File komutu, bir dosyanın türünü belirtir:

file fileName

Bunu bir satırda şu şekilde yapabilirsiniz:

stat fileName ; file fileName

Ancak, fileName iki kez yazmanız gerekir.

Her iki komutu da aynı giriş üzerinde (girişi veya girişin bir değişkenini iki kez yazmadan) nasıl uygulayabilirsiniz?

Linux'ta çıkışları nasıl yapacağımı biliyorum, ama girişleri nasıl aktarıyorsunuz?


14
Sadece açık olmak gerekirse, bunlar "girdi" değildir, argümanlardır (aka parametreler). Giriş< (dosyadan sola |giriş ) veya (akıştan sağa giriş ) aracılığıyla borulanabilir . Bir fark var.
goldilock

6
Sorunuza bir cevap değil, belki de sorununuza bir cevap: Bash'ta, yank-last-argkomut (varsayılan kısayol Meta-.veya Meta-_; Metagenellikle sol Altanahtar olmaktır ), önceki parametreyi önceki komut satırından geçerli olana kopyalayacaktır. Yani, stat fileNamesiz yürüttükten sonra yazın file [Meta-.]ve fileNametekrar yazmanıza gerek yok . Bunu her zaman kullanırım.
Dubu

2
Ve fark olduğunu <ve >olan yönlendirme değil, boru . Bir boru hattı iki işlemi birleştirirken, yönlendirme sadece stdin / stdout işlevini yeniden verir.
bsd

@bdowning: Evet, ama siz boruyu yönlendiriyorsunuz, yine de değilsiniz. Diskteki bir dosyadan okumak için bir boru hattının başlangıcını yönlendirebilir veya diskteki bir dosyaya yazılacak olan bir boru hattının sonunu yönlendirebilirsiniz. Hala borularda. ;-)
James Haigh

Yanıtlar:


21

İşte başka bir yol:

$ stat filename && file "$_"

Örnek:

$ stat /usr/bin/yum && file "$_"
  File: ‘/usr/bin/yum’
  Size: 801         Blocks: 8          IO Block: 4096   regular file
Device: 804h/2052d  Inode: 1189124     Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Context: system_u:object_r:rpm_exec_t:s0
Access: 2014-06-11 22:55:53.783586098 +0700
Modify: 2014-05-22 16:49:35.000000000 +0700
Change: 2014-06-11 19:15:30.047017844 +0700
 Birth: -
/usr/bin/yum: Python script, ASCII text executable

Bu bashve çalışır zsh. Bu da çalışır mkshve dashancak etkileşimli olduğunda. AT&T ksh’de, sadece ondan file "$_"farklı bir hattayken çalışır stat.


1
!$Tek nedeni çalışmaz !$(böylece daha önce girilen komut satırının değil istatistik komutunun) son tarih olayının son kelimeye Genişlediğinde.
Stéphane Chazelas

@ StéphaneChazelas: Ah, evet, benim hatam, sildim.
cuonglm

32

Muhtemelen bunun için parmak eklemlerimi yağdıracak, işte hile yapıyormuş gibi görünen bash ayraç genişlemesi ve değerlendirmenin hack bir birleşimi.

eval {stat,file}" fileName;"

8
Üzgünüm ama dayanamıyorum .
Andreas Wiese

2
ayracı genişleme kökenli cshdeğil bash. bashkopyaladım ksh. Bu kod tüm csh, tcsh, ksh, zsh, fish and bash'ta çalışacaktır.
Stéphane Chazelas

1
Ne yazık ki alıntı nedeniyle fileName "otomatik olarak tamamlama" (otomatik tamamlama) özelliğini kaybedersiniz.
Lonniebiz

Üzgünüm ama dayanamadım ya
tomd

üzgünüm burada sorun ne eval? (En iyi yorumlara atıfta
bulunun

28

İle zshanonim işlevleri kullanabilirsiniz:

(){stat $1; file $1} filename

İle eslambdas:

@{stat $1; file $1} filename

Ayrıca şunları da yapabilirsiniz:

{ stat -; file -;} < filename

( erişim zamanını güncelleyecek statilk yapan kişi file).

Ben yapardım:

f=filename; stat "$f"; file "$f"

Yine de, değişkenler bunun için var.


3
{ stat -; file -;} < filenamebüyü. statstdin dosyasının bir dosyaya bağlı olup olmadığını kontrol etmek ve dosyanın özniteliklerini rapor etmek için kontrol edilmeli mi? Muhtemelen işaretçiyi stdin üzerinde ilerletmediği için filestdin içeriklerini koklama imkanı
iruvar

1
@ 1_CR, evet. fd 0'da bir şeyler yapmasını stat -söyler stat.fstat()
Stéphane Chazelas

4
{ $COMMAND_A -; $COMMAND_B; } < filename$ COMMAND_A aslında herhangi bir giriş okur ise varyant genel durumda çalışmaz; örneğin { cat -; cat -; } < filename, dosya adı içeriğini yalnızca bir kez yazdıracaktır.
Max Nanasy

2
stat -coreutils-8.0'dan (Ekim 2009) bu yana özel olarak ele alınır, bundan önceki sürümlerde bir hata alırsınız ( -önceki bir denemeden çağrılan bir dosyanız yoksa , bu durumda yanlış sonuçlar alırsınız ...)
mr .spuratic

1
@ mr.spuratic. Evet ve BSD'ler, IRIX ve zsh istatistikleri de bunu tanımaz. Zsh ve IRIX stat ile stat -f0bunun yerine kullanabilirsiniz .
Stéphane Chazelas

9

Bu cevapların hepsi bana büyük ölçüde veya daha azına göre komut dosyası gibi görünüyor. Gerçekten komut dosyası içermeyen ve klavyede bir kabuk içine yazdığınızı varsayan bir çözüm için, şunları deneyebilirsiniz:

statfile Enter
dosyası Esc_   Enter

Bash, zsh ve ksh, en azından hem vi hem de emacs modlarında, Escape tuşuna basıp ardından alt çizgi, son satırı önceki satırdan geçerli satır için düzenleme arabelleğine ekler.

Veya, geçmiş değişimini kullanabilirsiniz:

statfile Enter
dosyası! $Enter

Bash'ta, zsh, csh, tcsh ve diğer birçok kabuk, !$geçerli komut satırı ayrıştırıldığında önceki komut satırının son sözcüğü ile değiştirilir.


zsh / bash'da buna benzer başka seçenekler nelerdir Esc _? Resmi belgelerle bağlantı kurar mısınız?
Abhijeet Rastogi


1
zsh: linux.die.net/man/1/zshzle ve "son kelimesi ekle" kelimesini
aratın

1
@shadyabhi ^ Hem bash hem de zsh'nin standart anahtar bağlantıları için bağlantılar. Bash'nin sadece readline kullandığını unutmayın, bu yüzden (çoğu) bash'ler, readline kütüphanesini kullanan herhangi bir uygulamada çalışacaktır.
godlygeek

3

Bunu makul bir şekilde bulmamın yolu, basitçe xargs kullanmak, bir dosya / boru alır ve içeriklerini bir program argümanına dönüştürür. Bu, bir akışı bölen ve iki veya daha fazla programa gönderen tee ile birleştirilebilir.

echo filename | tee >(xargs stat) >(xargs file) | cat

Diğer cevapların çoğundan farklı olarak, bu bash ve Linux altındaki diğer kabuklarda işe yarayacaktır. Bunun bir değişkenin iyi bir kullanım örneği olduğunu söyleyeceğim, ancak kesinlikle kullanamıyorsanız, bunu yalnızca borular ve basit uygulamalar ile (özellikle süslü bir kabuğunuzun olmadığı varsayılarak) yapmanın en iyi yoludur.

Ek olarak, bunu programdan sonraki programlarla aynı şekilde ekleyerek istediğiniz sayıda program için yapabilirsiniz.

DÜZENLE

Yorumlarda önerildiği gibi, bu cevabın bir kaç kusuru var, ilki çıktı taraması potansiyeli. Bu şekilde düzeltilebilir:

echo filename | tee >(xargs stat) >(( wait $!; xargs file )) | cat

Bu komutları sırayla çalıştırmaya zorlar ve çıktı asla geçilmez.

İkinci sorun, bazı mermilerde bulunmayan işlem ikamesinden kaçınmaktır, bu, tee yerine tpipe kullanılarak yapılabilir:

echo filename | tpipe 'xargs stat' | ( wait $!; xargs file )

Bu, neredeyse her kabuğa taşınabilir olmalı (umarım) ve diğer komandendeki sorunları çözer, ancak şu anki sistemimde mevcut tpipe olmadığı için bu sonuncuyu bellekten yazıyorum.


1
İşlem değiştirme, yalnızca çalışan ksh(kamu malı değişkenleri değil) bashve ksh özelliğidir zsh. Aynı anda çalışacağını statve fileaynı zamanda çalışacağını unutmayın , bu nedenle büyük olasılıkla çıktıları iç içe geçmiş olur (Sanırım | catbu etkiyi sınırlandırmak için çıktı tamponlamasını zorlamak için kullanıyorsunuz ?).
Stéphane Chazelas

@ StéphaneChazelas Kedi hakkında haklısın, kullanırken, kullanırken hiçbir zaman interlaced giriş durumu üretmeyi başaramadım. Ancak, anlık olarak daha taşınabilir bir çözüm üretmek için bir şeyler deneyeceğim.
Vality

3

Bu cevap diğer birkaç taneye benzer:

stat dosya adı   && dosyası! #: 1

Bir önceki komutun son argümanını otomatik olarak tekrarlayan diğer cevapların aksine , bu, saymanızı gerektirir:

ls -ld dosya ismi   && dosyası! #: 2

Diğer cevapların bazılarının aksine, bu tırnak gerektirmez:

stat "useless cat"  &&  file !#:1

veya

echo Sometimes a '$cigar' is just a !#:3.

2

Bu, diğer cevaplar birkaç benzer, fakat daha taşınabilir bu bir ve çok daha basit bu bir :

sh -c 'stat "$ 0"; "$ 0" ' dosya adı

veya

sh -c 'stat "$ 1"; "$ 1" ' dosya adı

içinde sh atandı $0. Yazmak için üç karakterden daha fazla olmasına rağmen, bu (ikinci) form tercih edilir çünkü $0bu satır içi komut dosyasına verdiğiniz ad budur. Örneğin, ne zaman fileveya statbulunamadığı veya herhangi bir nedenden dolayı yürütülemediği gibi, o betiğin kabuğu tarafından hata mesajlarında kullanılır .


Yapmak istiyorsan

stat dosya adı 1  dosya adı 2  dosya adı 3 …; dosya dosya adı 1  dosya adı 2  dosya adı 3

rastgele sayıda dosya için

sh -c 'stat "$ @"; dosya "$ @" 'sh dosyaismi 1  dosyaismi 2  dosya adı 3

hangi çalışır çünkü "$@"eşdeğerdir "$1" "$2" "$3" ….


Bu $0satır içi komut dosyasına vermek istediğiniz ad budur . Örneğin, bu betiğin kabuğu tarafından hata mesajlarında kullanılır. Ne zaman fileya statda bulunamadığı gibi, ya da herhangi bir nedenle yürütülemez. Demek burada anlamlı bir şey istiyorsun. İlham vermediyseniz, sadece kullanın sh.
Stéphane Chazelas

1

Bence en azından ne yapmaya çalıştığınla ilgili yanlış bir soru soruyorsun. statve filekomutlar, dosyanın içeriği değil, bir dosya adı olan parametreleri alıyor. Daha sonra, belirttiğiniz adla tanımlanan dosyayı okuyan komutta.

Bir borunun, biri girdi, diğeri çıktı olarak kullanılan iki ucu olması gerekir ve bu, ihtiyaç duyulana kadar farklı takımlarla farklı işlemler yapmanız gerekebileceğinden mantıklı olur. Boru çıkışlarının nasıl yapıldığını bildiğinizi ancak boru girişlerinin nasıl yapıldığını bilmediğinizi söylemek ilke olarak doğru değildir.

Durumunuzda, girişi bir dosya adı biçiminde sağlamanız gerektiğinden, hiçbir boruya ihtiyacınız yoktur. Ve bu aletler ( statve file) stdin okuyabilseler bile , boru burada tekrar ilgili değildir, çünkü girdi ikinciye ulaştığında hiçbir alet tarafından değiştirilmemelidir.


1

Bu, geçerli dizindeki tüm dosya adları için çalışmalıdır.

sh -c "$(printf 'stat -- "$1";file -- "$1";shift%.0b\n' *)" -- *

1
Dosya adlarının çift tırnak, ters eğik çizgi, dolar, ters çevirme, }... karakterleri içermediğini ve bununla başlamadığını varsayalım -. Bazı printfuygulamalarda (zsh, bash, ksh93) a vardır %q. Ile zsh, olabilir:printf 'stat %q; file %1$q\n' ./* | zsh
Stéphane Chazelas

@StephaneChezales - hardquote olasılığı dışında hepsi şimdi sabittir. Bekar olarak sed s///tahmin ediyorum, ama bunun kapsamı hakkında bir şeyler yapabilirim - ve eğer insanlar bunu dosya adlarına koyarlarsa pencereleri kullanmaları gerektiğini söylerdim .
mikeserv

@ StéphaneChazelas - nevermind - Bunların kullanımının ne kadar kolay olduğunu unuttum.
mikeserv

Açıkça söylemek gerekirse, bu şu soruyu cevaplamıyor: “Her iki komutu da aynı girdi üzerinde nasıl uygulayabilirsiniz (girişi yazmadan… iki kez )?” (Vurgu eklenmiş). Cevabınız geçerli dizindeki tüm dosyalar için geçerlidir - ve *iki kez içerir . Söyleyeceğin kadarıyla söyleyebilirsin stat -- *; file -- *.
Scott,

@Scott - giriş iki kez yazılmadı - *genişletildi. Genişleme ait *artı iki komutları her argüman in için * girdidir. Görmek? printf commands\ %s\;shift *
mikeserv

1

Burada 'girdi' için birkaç farklı referans var, bu yüzden önce onu anlamak için birkaç senaryo vereceğim. Soruya en kısa sürede yanıtınız :

stat testfile < <($1)> outputfile

Yukarıdakiler testfile üzerinde bir stat gerçekleştirecek, STDOUT 'u alacak (yönlendirme) olacak ve bunu bir sonraki özel fonksiyona (<() kısmına) dahil et, sonra neyin nihai sonuçlarını yeni bir dosyaya (çıkış dosyası) çıkar. Dosya çağrılır ve daha sonra bash yerleşiklerine başvurulur (yeni bir komut dizisine başlayana kadar her seferinde $ 1).

Sorunuz harika ve bunu yapmanın birkaç yanıtı ve yolu var, ancak gerçekten ne yaptığınızla gerçekten de değişiyor.

Mesela, oldukça kullanışlıdır. Bunun yaygın bir kullanımı, psuedo kodu zihniyetinde:

run program < <($output_from_program)> my_own.log

Bunu almak ve bu bilgiyi genişletmek, şöyle şeyler yaratmanıza izin verir:

ls -A; (while read line; do printf "\e[1;31mFound a file\e[0m: $line\n"; done) < <(/bin/grep thatword * | /usr/bin/tee -a files_that_matched_thatword)

Bu, şu anki dizininizde basit bir ls -A gerçekleştirecek, sonra ls -A'dan (ve işte zor olduğu yerlerde) her sonucun ne zaman geçileceğini söyleyiniz ve bu sonuçların her birinde "o kelimeyi" tutacaktır printf (içinde kırmızı olan) aslında içinde "thatword" olan bir dosya bulduysa. Ayrıca grep sonuçlarını, files_that_matched_thatword adlı yeni bir metin dosyasına kaydeder.

Örnek çıktı:

ls -A; (while read line; do printf "\e[1;31mFound a file\e[0m: $line\n"; done) < <(/bin/grep thatword * | /usr/bin/tee -a files_that_matched_thatword)

index.html

Tüm bunlar basitçe ls -A sonucunu yazdı, özel bir şey yok. Bu sefer grep için bir şeyler ekleyin:

echo "thatword" >> newfile

Şimdi tekrar çalıştır:

ls -A; (while read line; do printf "\e[1;31mFound a file\e[0m: $line\n"; done) < <(/bin/grep thatword * | /usr/bin/tee -a files_that_matched_thatword)

files_that_matched_thatword  index.html  newfile

Found a file: newfile:thatword

Şu anda aradığınızdan daha yorucu bir cevap olsa da, bunun gibi kullanışlı notları tutmanın gelecek çalışmalarınızda size daha fazla fayda sağlayacağına inanıyorum.

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.