Dlaczego to `grep -v` nie działa zgodnie z oczekiwaniami?

12

Mam dziwny problem związany z grep -vzapytaniami. Pozwól mi wyjaśnić:

Do wyświetlania połączeń używam 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)

Obecny ttymój terminal topts/0

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

Próbuję wykluczyć własne połączenie za pomocą grep -v $(tty | cut -f3-4 -d'/'). Oczekiwany wynik tego polecenia powinien być whobez mojego połączenia. Jednak wynik jest najbardziej nieoczekiwany:

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

Załączam $(...)cytaty i wydaje się, że to rozwiązuje problem „Brak takiego pliku lub katalogu”. Jednak moje połączenie jest nadal drukowane, mimo że moja tty ( pts/0) powinna zostać wykluczona:

$ 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)

W tej chwili nie mam pojęcia, dlaczego grepzapytanie działa nieprawidłowo.

być może może być
źródło
4
A może set -xnajpierw użyjesz ... Następnie uruchom polecenie i zobacz, co tak naprawdę próbujesz grep...
don_crissti
@don_crissti ah, rozumiem; to mówi mi, że tak naprawdę grep„nie jestem tty”. Jak sugerowałbyś, żebym to obejść?
maybeharry
użyj zmiennej: tldp.org/HOWTO/Bash-Prompt-HOWTO/x721.html
don_crissti

Odpowiedzi:

18

Zachary wyjaśnił źródło problemu.

Możesz z tym obejść

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

Byłoby to niewłaściwe, ponieważ na przykład, jeśli ten tty jest pts/1, ostatecznie wykluczasz wszystkie wiersze zawierające pts/10. Niektóre grepimplementacje mają -wopcję wyszukiwania słów

who | grep -vw pts/1

nie pasowałoby, pts/10ponieważ po pts/1nim nie następuje znak inny niż słowo.

Albo można użyć awkdo filtra od dokładnej wartości drugiego pola jak:

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

Jeśli chcesz to zrobić za pomocą jednego polecenia:

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

Oryginalny stdin jest powielany na deskryptorze pliku 3 i przywracany dla ttypolecenia.

Stéphane Chazelas
źródło
3
+1 za wymyślenie, jak to zrobić za pomocą jednego polecenia i wskazanie tego błędu.
Zachary Brady
Jeszcze jeden liniowiec:tty | cut -f3-4 -d'/' | xargs -I % sh -c "who | grep -v %"
axxis,
20

Ze strony informacyjnej tty.

„tty” wypisuje nazwę pliku terminala podłączonego do jego standardowego wejścia. Wyświetla „not tty”, jeśli standardowe wejście nie jest terminalem.

Problem polega na tym, że w twoim przykładzie sttyna tty to rura, a nie terminal.

Możesz zobaczyć z tego przykładu.

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

Aby obejść ten problem, możesz zrobić coś takiego.

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

Jest szybszy / bardziej wydajny sposób, jednak wymaga dwóch poleceń.

t=$(tty)
who|grep -wv "${t:5}"
Zachary Brady
źródło
@Christopher, czy jesteś jedynym zalogowanym na swoim komputerze?
Zachary Brady
@Christopher, dziwne. Więc who | grep -v "$(ps ax | grep "^$$" | awk '{ print $2 }')"produkuje oczekiwany wynik na moim urządzeniu i t=$(tty) who|grep -v "${t:5}"nie produkuje nic.
Zachary Brady
Jakiej powłoki / wersji używasz? GNU bash, version 4.1.2
Zachary Brady
2
ps ax | grep "^ *$$"może fałszować np. twoja powłoka to 123 i istnieje 1234; ps ax -otty= $$jest bardziej solidny i tylko jeden proces. Ale wolę twój ${t:5}lub Stephane'a ${t#/dev/}(lub substr(t,6))
dave_thompson_085
1
Nie dodawaj zastrzeżeń. Chociaż intencje są godne pochwały, tak naprawdę nie pomagają na odpowiedź. Jeśli ktoś zauważy błąd w odpowiedzi, po prostu edytuj odpowiedź, aby uwzględnić poprawkę.
terdon