Quiero hacer esto:
- ejecutar un comando
- capturar la salida
- seleccionar una línea
- seleccione una columna de esa línea
Solo como ejemplo, digamos que quiero obtener el nombre de comando de un $PID(tenga en cuenta que esto es solo un ejemplo, no estoy sugiriendo que esta sea la forma más fácil de obtener un nombre de comando de un ID de proceso; mi problema real es con otro comando cuyo formato de salida no puedo controlar).
Si corro psobtengo:
PID TTY TIME CMD
11383 pts/1 00:00:00 bash
11771 pts/1 00:00:00 ps
Ahora lo hago ps | egrep 11383y consigo
11383 pts/1 00:00:00 bash
Paso siguiente: ps | egrep 11383 | cut -d" " -f 4. La salida es:
<absolutely nothing/>
El problema es que cutcorta la salida por espacios simples, y como psagrega algunos espacios entre la 2da y 3ra columna para mantener cierta semejanza con una tabla, cutelige una cadena vacía. Por supuesto, podría usar cutpara seleccionar el séptimo y no el cuarto campo, pero cómo puedo saberlo, especialmente cuando la salida es variable y desconocida de antemano.

Respuestas:
Una forma fácil es agregar un pase de
trpara exprimir cualquier separador de campo repetido:$ ps | egrep 11383 | tr -s ' ' | cut -d ' ' -f 4fuente
tres más ligero queawkCreo que la forma más sencilla es usar awk . Ejemplo:
$ echo "11383 pts/1 00:00:00 bash" | awk '{ print $4; }' bashfuente
ps | awk "\$1==$PID{print\$4}"o (mejor)ps | awk -v"PID=$PID" '$1=PID{print$4}'. Por supuesto, en Linux simplemente podría hacerxargs -0n1 </proc/$PID/cmdline | head -n1oreadlink /proc/$PID/exe, pero de todos modos ...;de{ print $4; }necesaria? Eliminarlo parece no tener ningún efecto para mí en Linux, solo tengo curiosidad por su propósitoTenga en cuenta que la
tr -s ' 'opción no eliminará ningún espacio inicial. Si su columna está alineada a la derecha (como conpspid) ...$ ps h -o pid,user -C ssh,sshd | tr -s " " 1543 root 19645 root 19731 rootLuego, cortar dará como resultado una línea en blanco para algunos de esos campos si es la primera columna:
$ <previous command> | cut -d ' ' -f1 19645 19731A menos que lo preceda con un espacio, obviamente
$ <command> | sed -e "s/.*/ &/" | tr -s " "Ahora, para este caso particular de números pid (no nombres), hay una función llamada
pgrep:Funciones de shell
Sin embargo, en general, todavía es posible usar funciones de shell de manera concisa, porque hay algo interesante sobre el
readcomando:$ <command> | while read a b; do echo $a; doneEl primer parámetro a leer,
aselecciona la primera columna, y si hay más, se incluirá todo lo demásb. Como resultado, nunca necesita más variables que el número de su columna +1 .Entonces,
while read a b c d; do echo $c; doneluego generará la tercera columna. Como se indica en mi comentario ...
Una lectura canalizada se ejecutará en un entorno que no pasa variables al script de llamada.
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'`La solución de matriz
Entonces, terminamos con la respuesta de @frayser, que es usar la variable de shell IFS, que por defecto es un espacio, para dividir la cadena en una matriz. Sin embargo, solo funciona en Bash. Dash y Ash no lo apoyan. Me ha costado mucho dividir una cadena en componentes en un Busybox. Es bastante fácil obtener un solo componente (por ejemplo, usando awk) y luego repetirlo para cada parámetro que necesite. Pero luego terminas llamando repetidamente a awk en la misma línea, o usando repetidamente un bloque de lectura con eco en la misma línea. Lo cual no es eficiente ni bonito. Entonces terminas dividiéndote usando
${name%% *}y así. Te hace anhelar algunas habilidades de Python porque, de hecho, el script de shell ya no es muy divertido si la mitad o más de las funciones a las que estás acostumbrado se han ido. Pero puede asumir que incluso Python no se instalaría en un sistema de este tipo, y no lo fue ;-).fuente
echo "$a"yecho "$c"aunque.var=$(....... | { read a b c d; echo $c; }). Eso solo funciona para una sola (cadena), aunque en Bash puede dividirla en una matriz usandoar=($var)tratar
ps |& while read -p first second third fourth etc ; do if [[ $first == '11383' ]] then echo got: $fourth fi donefuente
Usando variables de matriz
set $(ps | egrep "^11383 "); echo $4o
A=( $(ps | egrep "^11383 ") ) ; echo ${A[3]}fuente
Similar a la solución awk de brianegge, aquí está el equivalente de Perl:
ps | egrep 11383 | perl -lane 'print $F[3]'-ahabilita el modo de división automática, que llena la@Fmatriz con los datos de la columna.Úselo
-F,si sus datos están delimitados por comas, en lugar de por espacios.El campo 3 se imprime ya que Perl comienza a contar desde 0 en lugar de 1
fuente
Obtener la línea correcta (ejemplo para la línea n. ° 6) se hace con head y tail y la palabra correcta (palabra n. ° 4) se puede capturar con awk:
command|head -n 6|tail -n 1|awk '{print $4}'fuente
awk NR=6 {print $4}sería un poco más eficienteawk NR==6 {print $4}* doh *Tu mando
ps | egrep 11383 | cut -d" " -f 4echa de menos un
tr -spara apretar espacios, como lo explica relajarse en su respuesta .Sin embargo, es posible que desee utilizar
awk, ya que maneja todas estas acciones en un solo comando:ps | awk '/11383/ {print $4}'Esto imprime la cuarta columna en las líneas que contienen
11383. Si desea que esto coincida11383si aparece al principio de la línea, puede decirps | awk '/^11383/ {print $4}'.fuente
En lugar de hacer todos estos greps y esas cosas, le aconsejo que utilice las capacidades ps para cambiar el formato de salida.
Obtiene la línea cmmand de un proceso con el pid especificado y nada más.
Esto es compatible con POSIX y, por lo tanto, puede considerarse portátil.
fuente
Bash
setanalizará toda la salida en parámetros de posición.Por ejemplo, con el
set $(free -h)comando,echo $7mostrará "Mem:"fuente
set $(sar -r 1 1);echo "${23}"awkes la mejor manera de hacerlo.bashy noawk.