12

I have a strange issue related to grep -v queries. Allow me to explain:

To display connections I use 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)

The current tty of my terminal is pts/0

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

I attempt to exclude my own connection using grep -v $(tty | cut -f3-4 -d'/'). The expected output of this command should be who, without my connection. However, the output is most unexpected:

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

I enclose the $(...) in quotes and that seems to fix the "No such file or directory" issue. However, my connection is still printed even though my tty (pts/0) should've been excluded:

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

As of this point, I have absolutely no idea why the grep query is malfunctioning.

3

2 Answers 2

20

From the tty info page.

'tty' prints the file name of the terminal connected to its standard input. It prints `not a tty' if standard input is not a terminal.

The problem is that in your example tty's stdin is a pipe, not your terminal.

You can see from this example.

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

To work around that you could do something like this.

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

There is a faster/more efficient way however it requires two commands.

t=$(tty)
who|grep -wv "${t:5}"
7
  • @Christopher are you the only one logged into your computer? Dec 8, 2016 at 13:56
  • @Christopher, weird. So the who | grep -v "$(ps ax | grep "^$$" | awk '{ print $2 }')" produces the expected output on my box and t=$(tty) who|grep -v "${t:5}" produces nothing. Dec 8, 2016 at 14:00
  • What shell/version are you using? GNU bash, version 4.1.2 Dec 8, 2016 at 14:03
  • 2
    ps ax | grep "^ *$$" could false-match e.g. your shell is 123 and 1234 exists; ps ax -otty= $$ is more robust and only one process. But I prefer your ${t:5} or Stephane's ${t#/dev/} (or substr(t,6)) Dec 8, 2016 at 18:10
  • 1
    Please don't add disclaimers. While the intent is laudable, they don't really help the answer. If someone points out a flaw in your answer, just edit your answer to incorporate the correction.
    – terdon
    Dec 8, 2016 at 19:45
18

Zachary has explained the source of the problem.

While you can work around it with

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

That would be wrong as for instance if that tty is pts/1, you would end up excluding all the lines containing pts/10. Some grep implementations have a -w option to do a word search

who | grep -vw pts/1

would not match on pts/10 because the pts/1 in there is not followed by a non-word character.

Or you could use awk to filter on the exact value of the second field like:

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

If you want to do it in one command:

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

The original stdin being duplicated onto file descriptor 3 and restored for the tty command.

2
  • 3
    +1 for figuring out how to do it in one command and pointing out that mistake. Dec 8, 2016 at 14:12
  • One more one liner: tty | cut -f3-4 -d'/' | xargs -I % sh -c "who | grep -v %"
    – axxis
    Dec 8, 2016 at 20:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .