¿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.

quizás pueda
fuente
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
use una variable: tldp.org/HOWTO/Bash-Prompt-HOWTO/x721.html
don_crissti

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.

Stéphane Chazelas
fuente
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}"
Zachary Brady
fuente
@ 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