Bash kullanarak komut çıktısını sütunlara böl?


88

Bunu yapmak istiyorum:

  1. bir komut çalıştır
  2. çıktıyı yakala
  3. bir hat seçin
  4. o satırın bir sütununu seçin

Sadece bir örnek olarak, diyelim ki komut adını a'dan almak istiyorum $PID(lütfen bunun bir örnek olduğunu unutmayın, bunun bir işlem kimliğinden bir komut adı almanın en kolay yolu olduğunu önermiyorum - asıl sorunum ile çıktı formatını kontrol edemediğim başka bir komut).

Koşarsam psşunu alırım:


  PID TTY          TIME CMD
11383 pts/1    00:00:00 bash
11771 pts/1    00:00:00 ps

Şimdi yapıyorum ps | egrep 11383ve alıyorum

11383 pts/1    00:00:00 bash

Sonraki adım: ps | egrep 11383 | cut -d" " -f 4. Çıktı:

<absolutely nothing/>

Sorun, cutçıktıyı tek boşluklarla kesmesi ve psbir tablonun benzerliğini korumak için 2. ve 3. sütunlar arasına boşluklar ekledikçe cutboş bir dize seçmesidir. Tabii ki, cut4. alanı değil 7. alanı seçmek için kullanabilirim , ancak özellikle çıktı değişken olduğunda ve önceden bilinmediğinde nasıl bilebilirim.


2
Awk (ve 25 karakter daha) kullanın.
Michael Foukarakis

Yanıtlar:


179

Kolay bir yol, trtekrarlanan alan ayırıcılarını sıkıştırmak için bir geçiş eklemektir :

$ ps | egrep 11383 | tr -s ' ' | cut -d ' ' -f 4

1
Bunu beğendim, görünüşe trgöre daha hafifawk
flybywire

3
Kabul etme eğilimindeydim, ama bunun nedeni awk öğrenmemiş olmam da olabilir. :)
gevşeyin

Çıkarma olarak ilgilendiğiniz PID'yi içeren bir PID süreciniz varsa çalışmayacaktır.
David Grayson

1
Ayrıca, bazı PID'ler solda boşluk dolgulu iken diğerleri değilse saha rahipleri çalışmayacaktır.
üçlü

68

Bence en basit yol awk kullanmaktır . Misal:

$ echo "11383 pts/1    00:00:00 bash" | awk '{ print $4; }'
bash

4
Orijinal soru ile uyumluluk için ps | awk "\$1==$PID{print\$4}"veya (daha iyi) ps | awk -v"PID=$PID" '$1=PID{print$4}'. Elbette, Linux'ta basitçe yapabilirsin xargs -0n1 </proc/$PID/cmdline | head -n1ya da readlink /proc/$PID/exe, ama her neyse ...
süre

Is ;içinde { print $4; }gerekli? Kaldırmanın Linux'ta benim için bir etkisi yok gibi görünüyor, sadece amacını merak ediyorum
igniteflow

@igniteflow, print deyiminden sonra eklemeye devam etmek isteseydiniz, komutun sonunu göstermez mi?
joshmcode

16

Lütfen tr -s ' 'seçeneğin baştaki herhangi bir boşluğu kaldırmayacağını unutmayın . psSütununuz sağa hizalıysa ( pid'de olduğu gibi ) ...

$ ps h -o pid,user -C ssh,sshd | tr -s " "
 1543 root
19645 root
19731 root

Daha sonra kesme, bu alanlardan bazıları için ilk sütun ise boş bir satırla sonuçlanacaktır:

$ <previous command> | cut -d ' ' -f1

19645
19731

Önünde bir boşluk bırakmadıkça, tabii ki

$ <command> | sed -e "s/.*/ &/" | tr -s " "

Şimdi, bu özel pid sayı durumu için (isimler değil), adında bir işlev var pgrep:

$ pgrep ssh


Kabuk işlevleri

Bununla birlikte, genel olarak, kabuk işlevlerini özlü bir şekilde kullanmak aslında hala mümkündür , çünkü readkomutla ilgili güzel bir şey vardır :

$ <command> | while read a b; do echo $a; done

Okunacak ilk parametre a, ilk sütunu seçer ve daha fazlası varsa, geri kalan her şey yerleştirilir b. Sonuç olarak, sütun +1 sayınızdan daha fazla değişkene asla ihtiyacınız olmaz .

Yani,

while read a b c d; do echo $c; done

daha sonra 3. sütunu çıkarır. Yorumumda belirtildiği gibi ...

Değişkenleri çağıran betiğe aktarmayan bir ortamda borulu okuma yürütülecektir.

out=$(ps whatever | { read a b c d; echo $c; })

arr=($(ps whatever | { read a b c d; echo $c $b; }))
echo ${arr[1]}     # will output 'b'`


Dizi Çözümü

Böylece, dizeyi bir diziye bölmek için varsayılan olarak bir boşluğa sahip olan kabuk değişkeni IFS'yi kullanmak olan @frayser'ın cevabını buluruz. Yine de sadece Bash'de çalışır. Dash ve Ash bunu desteklemiyor. Bir Busybox şeyinde bir dizgiyi bileşenlere ayırmakta gerçekten zorlandım. Tek bir bileşen elde etmek (örneğin awk kullanarak) ve sonra bunu ihtiyacınız olan her parametre için tekrarlamak yeterince kolaydır. Ama sonra aynı satırda tekrar tekrar awk'ı çağırırsınız veya aynı satırda yankılı bir okuma bloğu tekrar tekrar kullanırsınız. Etkili veya hoş değil. Böylece kullanarak bölme yaparsınız ${name%% *}ve bunun gibi. Bazı Python becerileri için özlem duymanıza neden olur, çünkü aslında alıştığınız özelliklerin yarısı veya daha fazlası kaybolduğunda kabuk komut dosyası yazmak artık pek eğlenceli değildir. Ancak python'un bile böyle bir sisteme kurulmayacağını ve böyle bir sistemde olmadığını varsayabilirsiniz ;-).


Sen değişken etrafında tırnak kullanmalıdır echo "$a"ve echo "$c"gerçi.
üçlü

Görünüşe göre her borulu blok kendi alt kabuğunda veya işleminde yürütülüyor ve çevreleyen bloğa herhangi bir değişken döndüremiyor musunuz? Yine de yankıladıktan sonra çıktısını elde edebilirsiniz. var=$(....... | { read a b c d; echo $c; }). Bu yalnızca tek bir (dizge) için işe ar=($var)
yarar

@tripleee Sürecin böyle bir aşamasında bunun bir sorun olduğunu düşünmüyorum. Buna ihtiyacın olup olmadığını yakında keşfedeceksin ve eğer bu bir noktada kırılırsa, bu bir öğrenme dersi. Ve sonra neden bu çift tırnak işaretlerini kullanmak zorunda kaldığınızı biliyorsunuz ;-). Ve artık başkalarından duyduğunuz bir şey değil. Ateşle oynamak! : D. : s.
Xennex81

ayrıntılı cevap: D
ncomputers

Bu, söylememem için bana çok yardımcı oldu.
Ivan X

4

Deneyin

ps |&
while read -p first second third fourth etc ; do
   if [[ $first == '11383' ]]
   then
       echo got: $fourth
   fi       
done

1
@flybywire - muhtemelen bu basit örnek için aşırıdır, ancak bu deyim, seçilen veriler üzerinde daha karmaşık işlemler yapmanız gerekiyorsa mükemmeldir.
James Anderson

Ayrıca, bu günlerde varsayılan komut dosyası kabuğunun genellikle bash olmadığını unutmayın.
David

2

Dizi değişkenlerini kullanma

set $(ps | egrep "^11383 "); echo $4

veya

A=( $(ps | egrep "^11383 ") ) ; echo ${A[3]}

2

Brianegge'in awk çözümüne benzer şekilde, işte Perl eşdeğeri:

ps | egrep 11383 | perl -lane 'print $F[3]'

-a@Fdiziyi sütun verileriyle dolduran otomatik bölme modunu etkinleştirir .
Kullanım -F,verilerinizin eğer boşlukla ayrılmış ziyade, virgülle ayrılmış olduğunu.

Perl 1 yerine 0'dan saymaya başladığından, alan 3 yazdırılır


1
Perl çözümünüz için teşekkür ederiz - otomatik bölmeyi bilmiyordum ve yine de perl'in diğer araçları sonlandırmak için bir araç olduğunu düşünüyorum ..;).
Gerard ONeill

1

Doğru satırı elde etmek (örneğin 6 numaralı satır için) baş ve kuyruk ile yapılır ve doğru kelime (kelime no. 4) awk ile yakalanabilir:

command|head -n 6|tail -n 1|awk '{print $4}'

Sadece, awk'nin satır bazında da seçebileceği gelecekteki okuyucular için not etmek gerekirse: awk NR=6 {print $4}biraz daha verimli olurdu
David Z

1
ve bununla tabii ki awk NR==6 {print $4}* doh * demek istedim
David Z

1

Emriniz

ps | egrep 11383 | cut -d" " -f 4

tr -sgevşeyin cevabında açıkladığı gibi, boşlukları sıkıştırmak için a'yı özlüyor .

Ancak, awktüm bu eylemleri tek bir komutta işlediği için belki kullanmak isteyebilirsiniz :

ps | awk '/11383/ {print $4}'

Bu, 4. sütunu içeren satırlarda yazdırır 11383 . Bunun eşleşmesini istiyorsanız 11383satırın başında görünüyorsa, diyebilirsinizps | awk '/^11383/ {print $4}' .


0

Tüm bu grepleri ve şeyleri yapmak yerine, çıktı formatını değiştirmek için ps yeteneklerini kullanmanızı tavsiye ederim.

ps -o cmd= -p 12345

Belirtilen pid ile bir işlemin cmmand satırını alırsınız ve başka hiçbir şey yoktur.

Bu POSIX uyumludur ve bu nedenle taşınabilir olarak kabul edilebilir.


1
flybywire, ps'yi örnek olarak kullandığını belirtir, soru bundan daha geneldir.
Ogre Mezmur 33

0

Bash'ler set tüm çıktıları konum parametrelerine ayrıştırır.

Örneğin, set $(free -h)komut ile echo $7"Mem:" gösterilecektir.


Bu yöntem, yalnızca komutun tek bir çıktı satırı olduğunda kullanışlıdır. Yeterince genel değil.
codeforester

Bu doğru değil, tüm çıktılar satırlardan bağımsız olarak konumsal parametrelere yerleştirilir. eski set $(sar -r 1 1); echo "${23}"
dman

Demek istediğim, çıktı hacimli ve birçok alana sahip olduğunda argümanın konumunu belirlemenin zor olmasıydı. awkbunu yapmanın en iyi yolu.
codeforester

Bu sadece başka bir çözüm. OP, bu tek kullanımlık durum için awk dilini öğrenmek istemeyebilir. Etiketlerin durumu var bash, değil awk.
dman
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.