Bir dosyanın ilk satırı _and_ ile belirli bir satırı nasıl elde edersiniz?


76

Gibi basit bir grep varsayalım:

$ psa aux | grep someApp
1000     11634 51.2  0.1  32824  9112 pts/1    SN+  13:24   7:49 someApp

Bu, çok fazla bilgi sağlar, ancak ps komutunun ilk satırı eksik olduğundan, bilgi için bir bağlam yoktur. Ben de ps'nin ilk satırının gösterilmesini tercih ederim:

$ psa aux | someMagic someApp
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
1000     11634 51.2  0.1  32824  9112 pts/1    SN+  13:24   7:49 someApp

Tabii ki, özellikle ps için grep bir regex ekleyebilirsiniz:

$ ps aux | grep -E "COMMAND|someApp"

Bununla birlikte, ilk aşamada da almak istediğim diğer durumlar olduğu için daha genel bir çözümü tercih ederim.

Görünüşe göre bu bir "stdmeta" dosya tanıtıcısı için iyi bir kullanım durumudur .


9
Bu cevapların gerektirdiği karmaşıklık, Unix felsefesinin "bir şeyi yap ve iyi yap" felsefesinin bazen kullanılabilirlik yardası çubuğu ile ölçüldüğünde bizi nasıl başarısız ettiğini gösteriyor: tüm bu komutları bu ortak soruna uygulayacak kadar iyi bilmek (filtreleme işlemi bilgisi ve hala sütun etiketlerini görmek) yaklaşımın olumsuz tarafını gösterir: bazen işler birbirine çok iyi uymuyor. Araçları gibi nedeni budur ackyüzden faydalıdır ve neden perlfırladı geçmiş sed, awkvb popülaritesi: parçaları tutarlı bir bütün halinde özetlemek için daha önemlidir.
iconoclast

3
Tabii ki, bu belirli örnek için, -Cargümanı kullanabilir psve onu grep'e sokmanıza gerek kalmaz. örneğin ps u -C someAppveya hattaps u -C app1 -C app2 -C app3
cas

1
@conoclast: Tabii ki Unixy çözümü, her biri farklı filtre kümelerinden filtrelenecek olan birden fazla çizgiyi çoğaltabilecek bir araç olacaktır. ps aux | { head -1; grep foo; }Aşağıda @Nahuel Fouilleul tarafından belirtilen genelleştirilmiş bir versiyon ( Gerektiğinde yerinde hatırlayabileceğim tek çözüm budur)
Lie Ryan

@conoclast: Aletlerle ilgili deneyim ve araçların bilgisi eksikliği, aletlerin gerçekte iyi yaptıkları her zaman tamamen işe yaramaz görünecektir. Bir komutu iyi bilmek, kullanılabilirliğin kullanım çubuğunda, ince el kitabını ve alıştırmanın okunan çubuğundadır. Bu araçlar onlarca yıldır var. Çok güzel çalışırlar (ve temiz bir şekilde).
Ярослав Рахматуллин

@ ЯрославРахматуллин: Söylediklerimi tamamen yanlış anladığınızı düşünüyorum. (Belki İngilizce ilk diliniz olmadığı için?) "Kullanılabilirlik" UX ("kullanıcı deneyimi") ile ilgili değil (veya "kullanışlılık") ile ilgilidir. Basit bir işlem bu karmaşık olduğunda kullanılabilirlik acıyor olduğuna değinen olduğunu DEĞİL araçlar yararsız olduğunu söyleyerek aynı. Oldukça açıkçası onlar işe yaramaz değil. Aklı başında hiç kimse onların yararsız olduğunu söyleyemez.
iconoclast

Yanıtlar:


67

İyi bir yol

Normalde bunu grep ile yapamazsınız ancak diğer araçları kullanabilirsiniz. AWK daha önce de belirtilmişti ama şu şekilde de kullanabilirsiniz sed:

sed -e '1p' -e '/youpattern/!d'

Nasıl çalışır:

  1. Sed yardımcı programı her satırda ayrı ayrı çalışır ve her birinde belirli komutları çalıştırır. Birkaç -eseçenek belirterek birden çok komutunuz olabilir . Her komutu, bu komutun belirli bir satıra uygulanıp uygulanmayacağını belirten bir range parametresiyle hazırlayabiliriz.

  2. "1p" ilk komuttur. pNormalde tüm satırları basan komutu kullanır . Ancak, uygulanması gereken aralığı belirten sayısal bir değerle onu hazırlıyoruz. Burada, 1ilk satır anlamına gelir. Daha fazla satır yazdırmak istiyorsanız, yazdırılacak ilk satırın, yazdırılacak son satırın x,ypnerede xolduğunu kullanabilirsiniz y. Örneğin, ilk 3 satırı yazdırmak için,1,3p

  3. Sonraki komut dnormalde tüm satırları tampondan siler. Bu komuttan önce yourpatterniki /karakter arasına koyduk . pKomutun çalışması gereken adresleme adresleri (ilk önce hangi komutları yaptığımız gibi ) belirtmekteydi . Bu, komutun yalnızca eşleşen satırlar için çalışacağı anlamına gelir yourpattern. Bunun dışında, mantığını tersine çeviren komuttan !önce karakter kullanırız d. Böylece, belirtilen kalıba uymayan tüm satırları kaldıracak .

  4. Sonunda sed, tamponda kalan tüm satırları yazdıracaktır. Ancak, arabellekten uyuşmayan satırları kaldırdık, böylece yalnızca eşleşen satırlar yazdırılacak.

Özetlemek gerekirse: 1. satırı yazdırır, sonra da kalıbımızla eşleşmeyen tüm satırları girişten sileriz. Hatlar geri kalanı (yani sadece çizgiler basılır yapmak deseni eşleşir).

İlk satır problemi

Yorumlarda belirtildiği gibi, bu yaklaşımla ilgili bir sorun var. Belirtilen desen ilk satırla da eşleşirse, iki kez yazdırılır (bir defa pkomutla ve bir eşleşme nedeniyle bir kez). Bundan iki şekilde kurtulabiliriz:

  1. 1dSonra komut ekleme 1p. Daha önce de bahsettiğim gibi, dkomut satırları arabellekten siler ve 1 numaralı aralıklarla belirtiyoruz, bu yalnızca 1 satırı sileceği anlamına geliyor. Yani komut olurdused -e '1p' -e '1d' -e '/youpattern/!d'

  2. Yerine 1bkomutu kullanma 1p. Bu bir numara. bkomut, bir etiket tarafından belirtilen diğer komutlara geçmemizi sağlar (bu şekilde bazı komutlar atlanabilir). Ancak bu etiket belirtilmezse (örneğimizde olduğu gibi), komutların sonuna atlar, satırımızın geri kalan komutlarını görmezden gelir. Yani bizim durumumuzda, son dkomut bu satırı tampondan çıkarmaz.

Tam örnek:

ps aux | sed -e '1b' -e '/syslog/!d'

Noktalı virgül kullanarak

Bazı seduygulamalar, birden çok -eseçenek kullanmak yerine komutları ayırmak için noktalı virgül kullanarak bazı yazılar yazdırabilir . Bu yüzden taşınabilir olmayı umursamıyorsanız, komut olacaktır ps aux | sed '1b;/syslog/!d'. En azından GNU sedve busyboxuygulamaları çalışır .

Çılgın yol

İşte, ancak, grep ile bunu yapmak için oldukça çılgın bir yol. Kesinlikle en uygun değil, bunu sadece öğrenme amaçlı gönderiyorum, ancak sisteminizde başka bir aracınız yoksa, örneğin kullanabilirsiniz:

ps aux | grep -n '.*' | grep -e '\(^1:\)\|syslog'

Nasıl çalışır

  1. İlk -nönce, her satırdan önce satır numarası eklemek için bu seçeneği kullanırız. Eşleştiğimiz tüm çizgileri .*, her şeyi, hatta boş çizgiyi numaralandırmak istiyoruz . Yorumlarda önerildiği gibi, '^' ile eşleştirebiliriz, sonuç aynıdır.

  2. Sonra genişletilmiş düzenli ifadeler kullanıyoruz, böylece \|OR olarak çalışan özel karakterleri kullanabiliriz . Bu nedenle, satır 1:(ilk satır) ile başlarsa veya desenimizi içeriyorsa (bu durumda syslog) eşleşiriz .

Satır numaraları sorunu

Şimdi sorun şu ki, çıktımızda bu çirkin satır numaralarını alıyoruz. Eğer bu bir problemse, bunları şu şekilde kaldırabiliriz cut:

ps aux | grep -n '.*' | grep -e '\(^1:\)\|syslog' | cut -d ':' -f2-

-dseçenek sınırlayıcıyı -fbelirtir, yazdırmak istediğimiz alanları (veya sütunları) belirtir. Bu yüzden her :karakterdeki her satırı kesmek ve sadece 2. ve sonraki tüm sütunları yazdırmak istiyoruz . Bu, ayırıcıyla ilk sütunu etkin bir şekilde kaldırır ve tam olarak ihtiyacımız olan şey budur.


4
Satır numaralandırması da yapılabilir cat -nve bunun için istismar edilen bir grepte olduğu gibi daha net görünür.
Alfe

1
nlboş satırları saymaz (ancak satır numarası olmadan yazdırır), cat -nnumaralandırmayı önceki boşluklarla biçimlendirir, boş satırları hiç çıkarmaz grep -n .ve iki nokta üst üste ekler. Hepsi var ... er ... özellikleri ;-)
Alfe

2
Çok eğitici iyi yazılmış cevap. "Pretend" (başlangıcın yakınında) yerine "Prepend" yazmayı denedim ama daha fazla değişiklik yapmak istedim ve gönderinizdeki rasgele pisliği değiştirmek istemedim, bu yüzden bunu düzeltmek isteyebilirsiniz.
Bill K

2
ps aux | sed '1p;/pattern/!d'Eğer maç iki kez ilk çizgi yazdırır deseni . En kullanılacak olan bkomut: ps aux | sed -e 1b -e '/pattern/!d'. cat -nPOSIX değil. grep -n '^'her satırı numaralandırır (boş satırları olmayan ps çıkışı için bir sorun değil). nl -ba -d $'\n'her çizgiyi numaralar.
Stéphane Chazelas 13:12

2
Not 1b;..."b" den sonra başka herhangi bir komut olamaz, taşınabilir ne de POSIX değildir, bu yüzden bir yeni satır veya başka -e ifadesini gerekir.
Stéphane Chazelas

58

awkBunun yerine kullanmak hakkında ne düşünüyorsunuz grep?

chopper:~> ps aux | awk 'NR == 1 || /syslogd/'
USER              PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
root               19   0.0  0.0  2518684   1160   ??  Ss   26Aug12   1:00.22 /usr/sbin/syslogd
mrb               574   0.0  0.0  2432852    696 s006  R+    8:04am   0:00.00 awk NR == 1 || /syslogd/
  • NR == 1: Kayıt sayısı == 1; yani. ilk satır
  • ||: veya:
  • /syslogd/: Aranacak kalıp

pgrepKullanıcıya yönelik çıktılardan ziyade komut dosyaları için daha fazla olmasına rağmen, bakmaya da değer olabilir . Yine de grepkomutun kendisinin çıktıda görünmesini engeller.

chopper:~> pgrep -l syslogd
19 syslogd

Çok hoş teşekkürler. Bu aynı zamanda gelecekteki genişleme için güzel bir betiktir.
dotancohen

Bana biraz garip öğrenmem gerek. çok hoş.
user606723 13:12

30
ps aux | { read line;echo "$line";grep someApp;}

EDIT: yorumlardan sonra

ps aux | { head -1;grep someApp;}

Gerçi head -1bütün girdi okurdum ama bunu test ettikten sonra, çok çalışır.

{ head -1;grep ok;} <<END
this is a test
this line should be ok
not this one
END

çıktı

this is a test
this line should be ok

2
Fikir doğrudan bash olarak dile getirildi. Bunun için birden fazla başparmak vermek istiyorum. { IFS='' read line; ... }Başlığın boşluklarla başlaması durumunda kullanabilirim .
Alfe

Bu tam olarak soruna doğrudan saldırıyor. Güzel!
dotancohen

3
Sadece head -1read / echo combo yerine kullanırdım .
chepner

1
Eh, head -n1benim bash üzerinde çalışır . Bu muhtemelen uygulamaya özel olabilir. Kafam bu durumda tüm girişi okumuyor, yalnızca ilk satırı girip geri kalanını giriş arabelleğinde bırakıyor.
Krzysztof Adamski

2
head -n1daha kısa, ancak POSIX özelliği bile, girişinin ne kadarının okunmasına izin verildiğine bağlı olarak sessiz, bu nedenle belki read line; echo $linede daha taşınabilir.
chepner,

14

Ps iç filtre desteği

Bash işlemi istediğinizi varsayalım:

ps -C bash -f

Adı verilen tüm işlemleri listeler bash.


Teşekkürler, bunu bilmek güzel. Bununla birlikte, diğerlerinin yanı sıra pythondan başlayan komut dosyalarını bulamaz.
dotancohen

6

Başlığı stderr'e gönderme eğilimindeyim :

ps | (IFS= read -r HEADER; echo "$HEADER" >&2; cat) | grep ps

Bu genellikle insan okuma amaçlı yeterlidir. Örneğin:

  PID TTY          TIME CMD
 4738 pts/0    00:00:00 ps

Parantezli kısım genel kullanım için kendi senaryosuna girebilir.

Çıktının daha fazla borulanabilmesi ( sortvb.) Ve başlığın üstte kalması için ilave bir kolaylık vardır .


5

Ayrıca kullanabilirsiniz teeve head:

ps aux | tee >(head -n1) | grep syslog

Bununla birlikte, sinyalleri teegöz ardı edemediği sürece SIGPIPE(bkz. Buradaki tartışma ), bu yaklaşımın güvenilir olması için bir geçici çözüm gerektirdiğini unutmayın. Çözüm, SIGPIPE sinyallerini görmezden gelmektir, bu örneğin kabuk gibi kabuklarda yapılabilir:

trap '' PIPE    # ignore SIGPIPE
ps aux | tee >(head -n1) 2> /dev/null | grep syslog
trap - PIPE     # restore SIGPIPE handling

Ayrıca çıktı sırasının garanti edilmediğine dikkat edin .


Çalışmak için buna güvenmezdim, ilk çalıştırdığımda (zsh) grep sonuçlarının altında sütun başlıkları üretti. İkinci kez iyiydi.
Rqomey

1
Henüz bu görmedim, ama güvenilirliği artırmanın tek yolu önce boru hattında küçük bir gecikme eklemek için grep: | { sleep .5; cat }.
Thor

2
Eşzamanlılık sorunlarını önlemek için uyku ekleme her zaman bir kesmek. Bu işe yarayabilse de, karanlık tarafa doğru bir adım. Bunun için -1.
Alfe

1
Bu cevabı denemek için başka garip meselelerim de vardı, kontrol etmek
Rqomey

Bu, tee'nin ilginç bir kullanımı, ancak güvenilmez buluyorum ve çoğu zaman yalnızca çıktı satırını yazdırıyor, ancak başlık satırını yazdırmıyor.
dotancohen

4

Belki de iki pskomut en kolay olurdu.

$ ps aux | head -1 && ps aux | grep someApp
USER             PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
100         3304   0.0  0.2  2466308   6476   ??  Ss    2Sep12   0:01.75 /usr/bin/someApp

2
Bu çözümü sevmiyorum, çünkü ilk ve ikinci ps auxçağrı arasındaki durum değişebilir çünkü … Statik ilk satırı istiyorsanız, neden manuel olarak yankılanmıyorsunuz?
Shadur

1
İki arama arasındaki değişiklikler bu durumda rahatsız edilmez . Birincisi, yalnızca her zaman ikincisinin çıkışına uygun olacak başlığı sağlayacaktır.
Alfe

2
Bunun neden reddedildiğini anlamıyorum, bu kesinlikle uygulanabilir bir seçenek. Upvoting.
dotancohen

4

Pidstat'ı aşağıdakilerle kullanabilirsiniz:

pidstat -C someApp
or
pidstat -p <PID>

Örnek:

# pidstat -C java
Linux 3.0.26-0.7-default (hostname)    09/12/12        _x86_64_

13:41:21          PID    %usr %system  %guest    %CPU   CPU  Command
13:41:21         3671    0.07    0.02    0.00    0.09     1  java

Daha Fazla Bilgi: http://linux.die.net/man/1/pidstat


Teşekkürler, bunu bilmek güzel. Bununla birlikte, diğerlerinin yanı sıra pythondan başlayan komut dosyalarını bulamaz.
dotancohen

4

Aşağıdakileri .bashrc dosyanıza koyun veya kopyalamak için önce kabuğa kopyalayın / yapıştırın.

function psls { 
ps aux|head -1 && ps aux|grep "$1"|grep -v grep;
}

Kullanım: psls [grep desen]

$ psls someApp
USER             PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
root              21   0.0  0.0  2467312   1116   ??  Ss   Tue07PM   0:00.17 /sbin/someApp

Eğer.

source ~/.bashrc

İşlev, kabuk komut satırında otomatik olarak tamamlanır. Başka bir cevapta belirttiğiniz gibi, bir çağrıyı ps'ye kaydetmek için ilk satırı bir dosyaya yönlendirebilirsiniz.


1
Güzel, bu tür bir işlevi yıllardır kullanıyorum. Ben buna benim sürümüpsl yalnızca çağrı psve grepher (ve ihtiyaç duymaz kez head).
Adam Katz

3

sıralama ancak üstbilgi satırını üstte tut

# print the header (the first line of input)
# and then run the specified command on the body (the rest of the input)
# use it in a pipeline, e.g. ps | body grep somepattern
body() {
    IFS= read -r header
    printf '%s\n' "$header"
    "$@"
}

Ve böyle kullan

$ ps aux | body grep someApp
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
1000     11634 51.2  0.1  32824  9112 pts/1    SN+  13:24   7:49 someApp

Teşekkürler, bu cevapların bazıları bu sorunun genel durumunu tartışıyor. Mükemmel!
dotancohen

3

Comp.unix.shell'deki çoğunlukla Janis Papanagnou'ya teşekkürler, aşağıdaki işlevi kullanıyorum:

function grep1 {
    IFS= read -r header && printf "%s\n" "$header"; grep "$@"
}

Bunun bir takım avantajları vardır:

  • Bash, zsh ve muhtemelen ksh ile çalışır
  • Grep için bırakma yerine geçen bir üründür, böylece istediğiniz bayrakları kullanmaya devam edebilirsiniz: büyük / -iküçük harf duyarlı eşleme, -Egenişletilmiş regex'ler vb.
  • Programlı bir satırın gerçekte eşleşip eşleşmediğini belirlemek istemeniz durumunda, daima grep ile aynı çıkış kodunu verir.
  • Giriş boşsa hiçbir şey yazdırmaz

Kullanım örneği:

$ ps -rcA | grep1 databases
  PID TTY           TIME CMD

$ ps -rcA | grep1 -i databases
  PID TTY           TIME CMD
62891 ??         0:00.33 com.apple.WebKit.Databases

2

İle başka bir yol gnu ed:

ed -s '!ps aux' <<< $'2,$v/PATTERN/d\n,p\nq\n'

veya, kabuk işlem değişimini destekliyorsa:

printf '%s\n' '2,$v/PATTERN/d' ,p q | ed -s <(ps aux)

yani:

2,$v/PATTERN/d  - remove all lines not matching pattern (ignore the header)
,p              - print the remaining lines
q               - quit

Daha taşınabilir, kabarık veya kabarcıklı gnu '!' değiştirme - tamponun çıktısını almak ve yalnızca aralıktaki eşleşmeyen çizgileri silmek ve sonucu yazdırmak için yalnızca edyerleşik olan kullanarak :rrps aux2,$

printf '%s\n' 'r !ps aux' '2,$v/PATTERN/d' ,p q | ed -s

Ve sedkabul edilen cevaptaki komutlar aynı zamanda kendileri ile eşleşen çizgiyi de sediçerdiğinden, bunu destekleyen -f-ve işlem değiştirmeyi destekleyen bir kabuk çalıştıracağım:

printf '%s\n' '2,${' '/PATTERN/!d' '}' | sed -f - <(ps aux)

bu da önceki edkomutlarla aynı şeyi yapar .


1

Perl yolu:

ps aux | perl -ne 'print if /pattern/ || $.==1'

Okumayı çok daha kolay sed, daha hızlı, istenmeyen satırları seçme riski yoktur.



0

Bu, yalnızca tam başlıkları içeren işlemler için geçerliyse, @ mrb'nin önerisini genişletin:

$ ps -f -p $(pgrep bash)
UID        PID  PPID  C STIME TTY      STAT   TIME CMD
nasha     2810  2771  0  2014 pts/6    Ss+    0:00 bash
...

pgrep bash | xargs ps -fpaynı sonucu ancak bir deniz kabuğu olmadan elde edersiniz. Başka bir formatlama gerekirse:

$ pgrep bash | xargs ps fo uid,pid,stime,cmd -p
  UID   PID STIME CMD
    0  3599  2014 -bash
 1000  3286  2014 /bin/bash
 ...

-2

Tam satır numaralarını biliyorsanız, perl ile kolaydır! Dosyadan 1. ve 5. satırları almak istiyorsanız, / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,5]){print}}' < /etc/passwd

Diğer satırları da almak istiyorsanız, sayılarını diziye eklemeniz yeterlidir.


1
Teşekkür ederim. OP'ye göre satırdaki metnin bir kısmını biliyorum, ancak satır numarasını bilmiyorum.
dotancohen

Bu, OP ile yakından ilgili olan bu kullanım durumunu ararken Google’da bir cevap olarak karşımıza çıkıyor;
Dagelf

1
Eğer durum buysa, yeni bir soru başlatmanızı ve bu cevapla cevaplamanızı şiddetle tavsiye ederim. Özellikle de bahsettiğin durumda, SE konusundaki sorularına cevap vermek gayet iyi. OP ile ilgili bir yorum yaparak devam edin ve yeni sorunuzla bağlantı kurun.
dotancohen

Böyle sorular var, ancak şu anda Google’da görünmüyorlar.
Dagelf

Dürüst olmak gerekirse, sonuç şu ki - cevabınız burada soruyu cevaplamıyor . @dotancohen haklı - eğer OP ile yakından ilgili olan bu kullanım vakasını ararken Google’da bir cevap olarak ortaya çıkarsa, ayrı bir soru sorun - bu yakından ilgili kullanım vakasını detaylandırarak - ve cevaplayın.
don_crissti
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.