Por que este `grep -v` não está funcionando como esperado?


12

Eu tenho um problema estranho relacionado a grep -vconsultas. Permita-me explicar:

Para exibir as conexões que eu 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)

A corrente ttydo meu terminal épts/0

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

Tento excluir minha própria conexão usando grep -v $(tty | cut -f3-4 -d'/'). A saída esperada deste comando deve ser who, sem a minha conexão. No entanto, a saída é mais inesperada:

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

Eu incluo as $(...)aspas e isso parece corrigir o problema "Esse arquivo ou diretório não existe". No entanto, minha conexão ainda é impressa, mesmo que meu tty ( pts/0) devesse ter sido excluído:

$ 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 deste ponto, não tenho absolutamente nenhuma idéia de por que a grepconsulta está com defeito.


4
Que tal usar set -xem primeiro lugar ... Em seguida, execute o seu comando e ver o que você está realmente tentando grep...
don_crissti

@don_crissti ah, entendo; está me dizendo que eu estou realmente grep"não é um tty". Como você sugeriria que eu contornasse isso?
perhapsmaybeharry

Respostas:


18

Zachary explicou a fonte do problema.

Enquanto você pode contornar isso com

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

Isso seria errado, por exemplo, se esse tty é pts/1, você acabaria excluindo todas as linhas que contêm pts/10. Algumas grepimplementações têm a -wopção de fazer uma pesquisa por palavras

who | grep -vw pts/1

não corresponderia pts/10porque o pts/1in não é seguido por um caractere que não seja palavra.

Ou você pode usar awkpara filtrar o valor exato do segundo campo, como:

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

Se você quiser fazer isso em um comando:

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

O stdin original está sendo duplicado no descritor de arquivo 3 e restaurado para o ttycomando.


3
+1 para descobrir como fazê-lo em um comando e apontar esse erro.
Zachary Brady

Mais um liner:tty | cut -f3-4 -d'/' | xargs -I % sh -c "who | grep -v %"
axxis 08/12/16

20

Na página de informações tty.

'tty' imprime o nome do arquivo do terminal conectado à sua entrada padrão. Imprime `não um tty 'se a entrada padrão não for um terminal.

O problema é que, no seu exemplo, o stdin do tty é um pipe, não o seu terminal.

Você pode ver neste exemplo.

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

Para contornar isso, você poderia fazer algo assim.

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

Existe uma maneira mais rápida / eficiente, porém requer dois comandos.

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

@ Christopher, você é o único logado no seu computador?
Zachary Brady

@ Christopher, estranho. Portanto, who | grep -v "$(ps ax | grep "^$$" | awk '{ print $2 }')"produz a saída esperada na minha caixa e t=$(tty) who|grep -v "${t:5}"não produz nada.
Zachary Brady

Qual shell / versão você está usando? GNU bash, version 4.1.2
Zachary Brady

2
ps ax | grep "^ *$$"poderia corresponder falsamente, por exemplo, seu shell é 123 e existe 1234; ps ax -otty= $$é mais robusto e apenas um processo. Mas eu prefiro o seu ${t:5}ou de Stephane ${t#/dev/}(ou substr(t,6))
dave_thompson_085

1
Não adicione avisos de isenção de responsabilidade. Embora a intenção seja louvável, eles realmente não ajudam a resposta. Se alguém apontar uma falha na sua resposta, basta editar sua resposta para incorporar a correção.
terdon
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.