Чому цей `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запит несправний.


4
Як щодо використання set -xспочатку ... Потім запустіть свою команду і подивіться, що ви насправді намагаєтесь grep...
— don_crissti

@don_crissti ах, бачу; це говорить мені, що я насправді grep"не tty". Як ти запропонував би мені обійти це?
— можливо, можебухарі

використовувати змінну: 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.

'tty' друкує ім'я файлу терміналу, підключеного до його стандартного входу. Він друкує `not tty ', якщо стандартний ввід не є терміналом.

Проблема полягає в тому, що у вашому прикладі stty 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}"

@Christopher Ви єдиний, хто ввійшов у свій комп’ютер?
— Захарій Брейді

@Christopher, дивно. Тож 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)
— Стефановим

1
Будь ласка, не додайте відмови. Хоча наміри похвальні, вони не дуже допомагають відповісти. Якщо хтось вказав на недолік вашої відповіді, просто відредагуйте свою відповідь, щоб включити виправлення.
— тердон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.