Aku ingin melakukan ini:
- jalankan perintah
- menangkap hasilnya
- pilih satu baris
- pilih kolom dari baris itu
Sebagai contoh, katakanlah saya ingin mendapatkan nama perintah dari a $PID
(harap dicatat ini hanya sebuah contoh, saya tidak menyarankan ini adalah cara termudah untuk mendapatkan nama perintah dari id proses - masalah saya sebenarnya adalah dengan perintah lain yang format keluarannya tidak dapat saya kendalikan).
Jika saya menjalankan ps
saya mendapatkan:
PID TTY TIME CMD
11383 pts/1 00:00:00 bash
11771 pts/1 00:00:00 ps
Sekarang saya lakukan ps | egrep 11383
dan dapatkan
11383 pts/1 00:00:00 bash
Langkah berikutnya: ps | egrep 11383 | cut -d" " -f 4
. Outputnya adalah:
<absolutely nothing/>
Masalahnya adalah cut
memotong output dengan spasi tunggal, dan saat ps
menambahkan beberapa spasi antara kolom ke-2 dan ke-3 untuk menjaga beberapa kemiripan tabel, cut
mengambil string kosong. Tentu saja, saya bisa menggunakan cut
untuk memilih bidang ke-7 dan bukan ke-4, tetapi bagaimana saya bisa tahu, khususnya ketika outputnya bervariasi dan tidak diketahui sebelumnya.
Jawaban:
Salah satu cara mudah adalah dengan menambahkan pass of
tr
untuk menekan pemisah bidang berulang apa pun:$ ps | egrep 11383 | tr -s ' ' | cut -d ' ' -f 4
sumber
tr
lebih ringan daripadaawk
Menurut saya cara paling sederhana adalah menggunakan awk . Contoh:
$ echo "11383 pts/1 00:00:00 bash" | awk '{ print $4; }' bash
sumber
ps | awk "\$1==$PID{print\$4}"
atau (lebih baik)ps | awk -v"PID=$PID" '$1=PID{print$4}'
. Tentu saja, di Linux Anda dapat melakukannyaxargs -0n1 </proc/$PID/cmdline | head -n1
ataureadlink /proc/$PID/exe
, tetapi bagaimanapun ...;
di{ print $4; }
diperlukan? Menghapusnya tampaknya tidak berpengaruh bagi saya di Linux, hanya ingin tahu tujuannyaHarap dicatat bahwa
tr -s ' '
opsi tidak akan menghapus satu spasi pun di depan. Jika kolom Anda rata kanan (sepertips
pid) ...$ ps h -o pid,user -C ssh,sshd | tr -s " " 1543 root 19645 root 19731 root
Kemudian pemotongan akan menghasilkan baris kosong untuk beberapa bidang tersebut jika itu adalah kolom pertama:
$ <previous command> | cut -d ' ' -f1 19645 19731
Kecuali jika Anda mendahului dengan spasi, tentu saja
$ <command> | sed -e "s/.*/ &/" | tr -s " "
Sekarang, untuk kasus nomor pid ini (bukan nama), ada fungsi yang disebut
pgrep
:Fungsi shell
Namun, secara umum sebenarnya masih memungkinkan untuk menggunakan fungsi shell secara ringkas, karena ada hal yang rapi tentang
read
perintah tersebut:$ <command> | while read a b; do echo $a; done
Parameter pertama untuk dibaca,,
a
memilih kolom pertama, dan jika ada lebih banyak, yang lainnya akan dimasukkanb
. Akibatnya, Anda tidak membutuhkan lebih banyak variabel daripada jumlah kolom +1 Anda .Begitu,
while read a b c d; do echo $c; done
kemudian akan menampilkan kolom ke-3. Seperti yang ditunjukkan dalam komentar saya ...
Pembacaan beralur akan dieksekusi di lingkungan yang tidak meneruskan variabel ke skrip pemanggil.
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'`
Solusi Array
Jadi kami kemudian berakhir dengan jawaban oleh @frayser yang menggunakan variabel shell IFS yang defaultnya ke spasi, untuk membagi string menjadi array. Ini hanya berfungsi di Bash. Dash dan Ash tidak mendukungnya. Saya mengalami kesulitan untuk membagi string menjadi beberapa komponen dalam Busybox. Cukup mudah untuk mendapatkan satu komponen (misalnya menggunakan awk) dan kemudian mengulanginya untuk setiap parameter yang Anda butuhkan. Tetapi kemudian Anda akhirnya berulang kali memanggil awk di baris yang sama, atau berulang kali menggunakan blok baca dengan gema di baris yang sama. Yang mana tidak efisien atau cantik. Jadi Anda akhirnya membelah menggunakan
${name%% *}
dan seterusnya. Membuat Anda merindukan beberapa keterampilan Python karena sebenarnya skrip shell tidak lagi menyenangkan jika setengah atau lebih fitur yang biasa Anda gunakan, hilang. Tetapi Anda dapat berasumsi bahwa bahkan python tidak akan diinstal pada sistem seperti itu, dan itu bukan ;-).sumber
echo "$a"
danecho "$c"
.var=$(....... | { read a b c d; echo $c; })
. Itu hanya berfungsi untuk satu (string), meskipun di Bash Anda dapat membaginya menjadi array menggunakanar=($var)
mencoba
ps |& while read -p first second third fourth etc ; do if [[ $first == '11383' ]] then echo got: $fourth fi done
sumber
Menggunakan variabel array
set $(ps | egrep "^11383 "); echo $4
atau
A=( $(ps | egrep "^11383 ") ) ; echo ${A[3]}
sumber
Mirip dengan solusi awk brianegge, berikut adalah padanan Perl:
ps | egrep 11383 | perl -lane 'print $F[3]'
-a
mengaktifkan mode autosplit, yang mengisi@F
array dengan data kolom.Menggunakan
-F,
jika data Anda dipisahkan dengan koma, bukan dipisahkan spasi.Bidang 3 dicetak karena Perl mulai menghitung dari 0 daripada 1
sumber
Mendapatkan baris yang benar (contoh untuk baris no. 6) dilakukan dengan head dan tail dan kata yang benar (kata no. 4) dapat ditangkap dengan awk:
command|head -n 6|tail -n 1|awk '{print $4}'
sumber
awk NR=6 {print $4}
akan sedikit lebih efisienawk NR==6 {print $4}
* doh *Perintah Anda
ps | egrep 11383 | cut -d" " -f 4
merindukan
tr -s
untuk memeras spasi, seperti yang dijelaskan oleh relaksasi dalam jawabannya .Namun, Anda mungkin ingin menggunakan
awk
, karena ini menangani semua tindakan ini dalam satu perintah:ps | awk '/11383/ {print $4}'
Ini mencetak kolom ke-4 di baris yang berisi
11383
. Jika Anda ingin ini cocok11383
jika muncul di awal baris, maka Anda bisa mengatakanps | awk '/^11383/ {print $4}'
.sumber
Alih-alih melakukan semua grep dan hal-hal ini, saya menyarankan Anda untuk menggunakan kapabilitas ps untuk mengubah format output.
Anda mendapatkan baris perintah dari sebuah proses dengan pid yang ditentukan dan tidak ada yang lain.
Ini adalah konforman POSIX dan karenanya dapat dianggap portabel.
sumber
Bash's
set
akan mengurai semua output menjadi parameter posisi.Misalnya, dengan
set $(free -h)
perintah,echo $7
akan muncul "Mem:"sumber
set $(sar -r 1 1)
;echo "${23}"
awk
adalah cara terbaik untuk melakukannya.bash
dan tidakawk
.