Почему этот `grep -v` не работает должным образом?

12

У меня странная проблема, связанная с grep -vзапросами. Позвольте мне объяснить:

Для отображения подключений я использую 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)

Ток ttyмоего терминалаpts/0

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

Я пытаюсь исключить мою собственную связь, используя grep -v $(tty | cut -f3-4 -d'/'). Ожидаемый вывод этой команды должен быть whoбез моего подключения. Однако вывод наиболее неожиданный:

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

Я заключил $(...)в кавычки, и это, кажется, решить проблему "Нет такого файла или каталога". Тем не менее, мое соединение все еще печатается, хотя мой tty ( pts/0) должен был быть исключен:

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

На данный момент я абсолютно не знаю, почему grepзапрос работает неправильно.

perhapsmaybeharry
источник
4
Как насчет использования set -xсначала ... Затем запустите свою команду и посмотрите, что вы на самом деле пытаетесь grep...
don_crissti
@don_crissti ах, понятно; это говорит мне, что я на самом деле grep"не TTY". Как бы вы посоветовали мне обойти это?
perhapsmaybeharry
используйте переменную: tldp.org/HOWTO/Bash-Prompt-HOWTO/x721.html
don_crissti

Ответы:

18

Захари объяснил источник проблемы.

Хотя вы можете обойти это с

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

Это было бы неправильно, как, например, если это tty pts/1, вы бы в конечном итоге исключили все строки, содержащие pts/10. В некоторых grepреализациях есть -wвозможность поиска по слову

who | grep -vw pts/1

не будет соответствовать, pts/10потому что за pts/1ним не следует символ, не состоящий из слов.

Или вы можете использовать awkдля фильтрации на точное значение второго поля, как:

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

Если вы хотите сделать это одной командой:

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

Исходный stdin дублируется на файловый дескриптор 3 и восстанавливается для ttyкоманды.

Стефан Шазелас
источник
3
+1 за выяснение того, как сделать это в одной команде и указание на эту ошибку.
Захари Брейди
Еще один лайнер:tty | cut -f3-4 -d'/' | xargs -I % sh -c "who | grep -v %"
Axxis
20

Со страницы tty info.

'tty' печатает имя файла терминала, подключенного к его стандартному входу. Он печатает `не tty ', если стандартный ввод не является терминалом.

Проблема в том, что в вашем примере tty's stdin - это канал, а не ваш терминал.

Вы можете видеть из этого примера.

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

Чтобы обойти это, вы можете сделать что-то вроде этого.

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

Существует более быстрый / более эффективный способ, однако он требует двух команд.

t=$(tty)
who|grep -wv "${t:5}"
Захари Брейди
источник
@ Кристофер, ты единственный, кто зашел на свой компьютер?
Захари Брэйди
@ Кристофер, странный. Таким образом, who | grep -v "$(ps ax | grep "^$$" | awk '{ print $2 }')"выдает ожидаемый результат на моей коробке и t=$(tty) who|grep -v "${t:5}"ничего не производит.
Захари Брейди
Какую оболочку / версию вы используете? GNU bash, version 4.1.2
Захари Брейди
2
ps ax | grep "^ *$$"может совпадать, например, ваша оболочка 123 и 1234 существует; ps ax -otty= $$является более надежным и только один процесс. Но я предпочитаю вашу ${t:5}или Стефана ${t#/dev/}(или substr(t,6))
dave_thompson_085
1
Пожалуйста, не добавляйте отказ от ответственности. Хотя намерения похвальны, они на самом деле не помогают ответ. Если кто-то указывает на ошибку в вашем ответе, просто отредактируйте свой ответ, чтобы включить исправление.
Terdon