Pregunta

Quiero hacer esto:

  1. ejecuta un comando
  2. captura la salida
  3. seleccione una línea
  4. seleccione una columna de esa línea

Solo como ejemplo, digamos que quiero obtener el nombre del 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 una identificación de proceso: mi problema real es con otro comando cuyo formato de salida no puedo controlar).

Si ejecuto ps obtengo:


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

Ahora hago ps | egrep 11383 y obtener

11383 pts/1    00:00:00 bash

Siguiente paso: ps | egrep 11383 | corte -d " " -f 4 . La salida es:

<absolutely nothing/>

El problema es que cut corta la salida en espacios individuales, y como ps agrega algunos espacios entre las columnas segunda y tercera para mantener cierto parecido con una tabla, < code> cut selecciona una cadena vacía. Por supuesto, podría usar cut para seleccionar el séptimo y no el cuarto campo, pero ¿cómo puedo saberlo, especialmente cuando el resultado es variable y desconocido de antemano?

¿Fue útil?

Solución

Una manera fácil es agregar un pase de tr para exprimir los separadores de campo repetidos:

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

Otros consejos

Creo que la forma más sencilla es usar awk . Ejemplo:

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

Tenga en cuenta que la opción tr -s '' no eliminará ningún espacio inicial único. Si su columna está alineada a la derecha (como con ps pid) ...

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

Luego, el corte dará como resultado una línea en blanco para algunos de esos campos si es la primera columna:

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

19645
19731

A 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 :

$ pgrep ssh


Funciones de shell

Sin embargo, en general, todavía es posible usar funciones de shell de manera concisa, porque hay algo interesante sobre el comando read :

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

El primer parámetro a leer, a , selecciona la primera columna, y si hay más, todo lo demás se colocará en b . 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; done

mostrará la tercera columna. Como se indica en mi comentario ...

Una lectura canalizada se ejecutará en un entorno que no pase 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 admiten. Me ha costado mucho dividir una cadena en componentes en una cosa de 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í sucesivamente. Te hace añorar algunas habilidades de Python porque, de hecho, las secuencias de comandos de shell ya no son muy divertidas si la mitad o más de las funciones a las que estás acostumbrado desaparecen. Pero puede suponer que incluso Python no se instalaría en dicho sistema, y ??no fue así ;-).

prueba

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

Similar a la solución awk de brianegge, aquí está el equivalente de Perl:

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

-a habilita el modo de división automática, que llena la matriz @F con los datos de la columna.
Utilice -F, si sus datos están delimitados por comas, en lugar de delimitados por espacios.

El campo 3 se imprime ya que Perl comienza a contar desde 0 en lugar de 1

Obtener la línea correcta (ejemplo para la línea no. 6) se realiza con cabeza y cola y la palabra correcta (palabra no. 4) se puede capturar con awk:

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

Uso de variables de matriz

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

o

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

En lugar de hacer todos estos greps y cosas, te aconsejo que uses las capacidades ps para cambiar el formato de salida.

ps -o cmd= -p 12345

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.

Tu comando

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

pierde un tr -s para exprimir espacios, como explica el desenrollado 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 esas líneas que contienen 11383 . Si desea que esto coincida con 11383 si aparece al comienzo de la línea, puede decir ps | awk '/ ^ 11383 / {print $ 4}' .

El

conjunto de Bash analizará toda la salida en parámetros de posición.

Por ejemplo, con el comando set $ (free -h) , echo $ 7 mostrará " Mem: "

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top