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.

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.

  • 3
    +1 for figuring out how to do it in one command and pointing out that mistake. – Zachary Brady Dec 8 '16 at 14:12
  • One more one liner: tty | cut -f3-4 -d'/' | xargs -I % sh -c "who | grep -v %" – axxis Dec 8 '16 at 20:55
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}"
  • @Christopher are you the only one logged into your computer? – Zachary Brady Dec 8 '16 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. – Zachary Brady Dec 8 '16 at 14:00
  • What shell/version are you using? GNU bash, version 4.1.2 – Zachary Brady Dec 8 '16 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)) – dave_thompson_085 Dec 8 '16 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 '16 at 19:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.