Chce to zrobić:
- uruchom polecenie
- uchwycić dane wyjściowe
- wybierz linię
- wybierz kolumnę tego wiersza
Jako przykład, powiedzmy, że chcę uzyskać nazwę polecenia z a $PID
(proszę zauważyć, że to tylko przykład, nie sugeruję, że jest to najłatwiejszy sposób uzyskania nazwy polecenia z identyfikatora procesu - mój prawdziwy problem polega na inne polecenie, którego formatem wyjściowym nie mogę kontrolować).
Jeśli biegnę ps
, dostaję:
PID TTY TIME CMD
11383 pts/1 00:00:00 bash
11771 pts/1 00:00:00 ps
Teraz robię ps | egrep 11383
i dostaję
11383 pts/1 00:00:00 bash
Następny krok: ps | egrep 11383 | cut -d" " -f 4
. Wynik to:
<absolutely nothing/>
Problem polega na tym, że cut
odcina dane wyjściowe o pojedyncze spacje i ps
dodaje spacje między drugą i trzecią kolumną, aby zachować pewne podobieństwo do tabeli, cut
wybiera pusty ciąg. Oczywiście mógłbym użyć cut
do wybrania siódmego, a nie czwartego pola, ale skąd mam wiedzieć, zwłaszcza gdy dane wyjściowe są zmienne i nieznane wcześniej.
Odpowiedzi:
Jednym prostym sposobem jest dodanie przebiegu w
tr
celu wyciśnięcia wszelkich powtarzających się separatorów pól:$ ps | egrep 11383 | tr -s ' ' | cut -d ' ' -f 4
źródło
tr
jest lżejszy niżawk
Myślę, że najprostszym sposobem jest użycie awk . Przykład:
$ echo "11383 pts/1 00:00:00 bash" | awk '{ print $4; }' bash
źródło
ps | awk "\$1==$PID{print\$4}"
lub (lepiej)ps | awk -v"PID=$PID" '$1=PID{print$4}'
. Oczywiście na Linuksie możesz po prostu zrobićxargs -0n1 </proc/$PID/cmdline | head -n1
lubreadlink /proc/$PID/exe
, ale w każdym razie ...;
w{ print $4; }
wymagane? Usunięcie tego wydaje się nie mieć na mnie żadnego wpływu na Linuksie, jestem po prostu ciekawy jego celuNależy pamiętać, że
tr -s ' '
opcja nie usunie żadnych pojedynczych spacji wiodących. Jeśli twoja kolumna jest wyrównana do prawej (jak w przypadkups
pid) ...$ ps h -o pid,user -C ssh,sshd | tr -s " " 1543 root 19645 root 19731 root
Wycinanie spowoduje powstanie pustej linii dla niektórych z tych pól, jeśli jest to pierwsza kolumna:
$ <previous command> | cut -d ' ' -f1 19645 19731
Chyba że poprzedzisz go spacją, oczywiście
$ <command> | sed -e "s/.*/ &/" | tr -s " "
Teraz, dla tego konkretnego przypadku numerów pid (nie nazw), istnieje funkcja o nazwie
pgrep
:Funkcje powłoki
Jednak ogólnie rzecz biorąc, nadal można używać funkcji powłoki w zwięzły sposób, ponieważ polecenie jest fajne
read
:$ <command> | while read a b; do echo $a; done
Pierwszy parametr do odczytania
a
,, wybiera pierwszą kolumnę, a jeśli jest ich więcej, wszystko inne zostanie wstawioneb
. W rezultacie nigdy nie potrzebujesz więcej zmiennych niż liczba Twojej kolumny +1 .Więc,
while read a b c d; do echo $c; done
wyświetli trzecią kolumnę. Jak wskazałem w moim komentarzu ...
Odczyt potokowy zostanie wykonany w środowisku, które nie przekazuje zmiennych do skryptu wywołującego.
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'`
Rozwiązanie macierzy
W rezultacie otrzymujemy odpowiedź od @frayser, która polega na użyciu zmiennej powłoki IFS, która domyślnie jest spacją, aby podzielić ciąg na tablicę. Działa jednak tylko w Bash. Dash i Ash tego nie obsługują. Naprawdę ciężko mi było rozdzielić łańcuch na komponenty w Busybox. Dość łatwo jest uzyskać pojedynczy komponent (np. Używając awk), a następnie powtórzyć to dla każdego potrzebnego parametru. Ale w końcu wielokrotnie wywołujesz awk w tej samej linii lub wielokrotnie używasz bloku odczytu z echem w tej samej linii. Co nie jest wydajne ani ładne. Więc kończysz rozdzielając używając
${name%% *}
i tak dalej. Sprawia, że tęsknisz za niektórymi umiejętnościami Pythona, ponieważ w rzeczywistości skrypty powłoki nie są już zbyt zabawne, jeśli zniknie połowa lub więcej funkcji, do których jesteś przyzwyczajony. Ale można założyć, że nawet python nie zostałby zainstalowany na takim systemie, a nie był ;-).źródło
echo "$a"
iecho "$c"
chociaż.var=$(....... | { read a b c d; echo $c; })
. To działa tylko dla pojedynczego (ciągu), chociaż w Bash możesz podzielić go na tablicę za pomocąar=($var)
próbować
ps |& while read -p first second third fourth etc ; do if [[ $first == '11383' ]] then echo got: $fourth fi done
źródło
Używanie zmiennych tablicowych
set $(ps | egrep "^11383 "); echo $4
lub
A=( $(ps | egrep "^11383 ") ) ; echo ${A[3]}
źródło
Podobnie jak w rozwiązaniu awk brianegge, tutaj jest odpowiednik Perla:
ps | egrep 11383 | perl -lane 'print $F[3]'
-a
włącza tryb autosplit, który zapełnia@F
tablicę danymi z kolumny.Posługiwać się
-F,
jeśli dane są rozdzielane przecinkami, a nie spacjami.Pole 3 jest drukowane, ponieważ Perl zaczyna liczyć od 0, a nie 1
źródło
Uzyskanie poprawnej linii (na przykład dla linii nr 6) odbywa się za pomocą głowy i ogona, a prawidłowe słowo (słowo nr 4) można przechwycić za pomocą awk:
command|head -n 6|tail -n 1|awk '{print $4}'
źródło
awk NR=6 {print $4}
byłoby trochę bardziej wydajnieawk NR==6 {print $4}
* doh *Twoja komenda
ps | egrep 11383 | cut -d" " -f 4
pomija a,
tr -s
aby ścisnąć spacje, jak wyjaśnia relax w swojej odpowiedzi .Możesz jednak chcieć użyć
awk
, ponieważ obsługuje wszystkie te akcje w jednym poleceniu:ps | awk '/11383/ {print $4}'
Spowoduje to wypisanie czwartej kolumny w wierszach zawierających
11383
. Jeśli chcesz, aby to pasowało,11383
jeśli pojawia się na początku wiersza, możesz powiedziećps | awk '/^11383/ {print $4}'
.źródło
Zamiast robić te wszystkie grepsy i inne rzeczy, radziłbym użyć możliwości ps zmiany formatu wyjściowego.
Otrzymasz wiersz cmmand procesu z określonym pid i nic więcej.
Jest to zgodne z POSIX i dlatego może być uważane za przenośne.
źródło
Bash
set
przetworzy wszystkie dane wyjściowe na parametry pozycji.Na przykład
set $(free -h)
polecenieecho $7
spowoduje wyświetlenie „Mem:”źródło
set $(sar -r 1 1)
;echo "${23}"
awk
to najlepszy sposób, aby się do tego zabrać.bash
a nieawk
.