¿Por qué este `grep -v` no funciona como se esperaba?


12

Tengo un problema extraño relacionado con las grep -vconsultas. Permítame explicarle:

Para mostrar las conexiones que uso who:

$ who
harry    pts/0        2016-12-08 20:41 (192.168.0.1)
james    pts/1        2016-12-08 19:28 (192.168.0.1)
timothy  pts/2        2016-12-08 02:44 (192.168.0.1)

La corriente ttyde mi terminal espts/0

$ tty
/dev/pts/0
$ tty | cut -f3-4 -d'/'
pts/0

Intento excluir mi propia conexión usando grep -v $(tty | cut -f3-4 -d'/'). El resultado esperado de este comando debería ser who, sin mi conexión. Sin embargo, la salida es más inesperada:

$ who | grep -v $(tty | cut -f3-4 -d'/')
grep: a: No such file or directory
grep: tty: No such file or directory

Adjunto las $(...)comillas y eso parece solucionar el problema "No existe tal archivo o directorio". Sin embargo, mi conexión todavía se imprime a pesar de que mi tty ( pts/0) debería haberse excluido:

$ who | grep -v "$(tty | cut -f3-4 -d'/')"
harry    pts/0        2016-12-08 20:41 (192.168.0.1)
james    pts/1        2016-12-08 19:28 (192.168.0.1)
timothy  pts/2        2016-12-08 02:44 (192.168.0.1)

A partir de este punto, no tengo ni idea de por qué la grepconsulta no funciona correctamente.


44
¿Cómo sobre el uso de set -xprimera ... A continuación, ejecute el comando y ver lo que realmente está tratando de grep...
don_crissti

@don_crissti ah, ya veo; me dice que en realidad estoy diciendo grep"no es un tty". ¿Cómo sugerirías que evite esto?
maybemaybeharry

Respuestas:


18

Zachary ha explicado la fuente del problema.

Si bien puedes solucionarlo con

tty=$(tty)
tty_without_dev=${tty#/dev/}
who | grep -v "$tty_without_dev"

Eso sería incorrecto, por ejemplo, si ese tty es pts/1, terminaría excluyendo todas las líneas que lo contienen pts/10. Algunas grepimplementaciones tienen la -wopción de hacer una búsqueda de palabras

who | grep -vw pts/1

no coincidiría pts/10porque la entrada pts/1no está seguida de un carácter que no sea de palabra.

O puede usar awkpara filtrar el valor exacto del segundo campo como:

who | awk -v "tty=$tty_without_dev" '$2 != tty'

Si quieres hacerlo con un solo comando:

{ who | awk -v "tty=$(tty<&3)" '$2 != substr(tty,6)'; } 3<&0

El stdin original se duplica en el descriptor de archivo 3 y se restaura para el ttycomando.


3
+1 para descubrir cómo hacerlo en un comando y señalar ese error.
Zachary Brady

Un trazador de líneas más:tty | cut -f3-4 -d'/' | xargs -I % sh -c "who | grep -v %"
axxis

20

Desde la página de información de tty.

'tty' imprime el nombre del archivo del terminal conectado a su entrada estándar. Imprime 'no un tty' si la entrada estándar no es un terminal.

El problema es que, en su ejemplo, el stdin de tty es una tubería, no su terminal.

Puedes ver en este ejemplo.

$ tty
/dev/pts/29
$ echo | tty 
not a tty

Para evitar eso, podrías hacer algo como esto.

who | grep -wv "$(ps ax | awk "\$1 == $$ {print \$2}" )"

Hay una forma más rápida / eficiente, sin embargo, requiere dos comandos.

t=$(tty)
who|grep -wv "${t:5}"

@ Christopher ¿Eres el único que inició sesión en tu computadora?
Zachary Brady

@Christopher, raro. Entonces who | grep -v "$(ps ax | grep "^$$" | awk '{ print $2 }')"produce el resultado esperado en mi caja y t=$(tty) who|grep -v "${t:5}"no produce nada.
Zachary Brady

¿Qué shell / versión estás usando? GNU bash, version 4.1.2
Zachary Brady

2
ps ax | grep "^ *$$"podría falsa coincidencia, por ejemplo, su shell es 123 y 1234 existe; ps ax -otty= $$es más robusto y solo un proceso. Pero prefiero tu ${t:5}o la de Stephane ${t#/dev/}(o substr(t,6))
dave_thompson_085

1
Por favor no agregue descargos de responsabilidad. Si bien la intención es loable, en realidad no ayudan a la respuesta. Si alguien señala una falla en su respuesta, simplemente edite su respuesta para incorporar la corrección.
terdon
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.