为什么这个 `grep -v` 没有按预期运行?

per*_*rry 12 command-line grep tty

我有一个与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)
Run Code Online (Sandbox Code Playgroud)

tty我的终端的电流是pts/0

$ tty
/dev/pts/0
$ tty | cut -f3-4 -d'/'
pts/0
Run Code Online (Sandbox Code Playgroud)

我尝试使用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
Run Code Online (Sandbox Code Playgroud)

我将$(...)引号括起来,这似乎解决了“没有这样的文件或目录”问题。但是,即使我的 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)
Run Code Online (Sandbox Code Playgroud)

到目前为止,我完全不知道为什么grep查询出现故障。

Zac*_*ady 20

从 tty 信息页面。

'tty' 打印连接到其标准输入的终端的文件名。如果标准输入不是终端,它会打印“not a tty”。

问题是在您的示例中,tty 的 stdin 是一个管道,而不是您的终端。

从这个例子中可以看出。

$ tty
/dev/pts/29
$ echo | tty 
not a tty
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题,你可以做这样的事情。

who | grep -wv "$(ps ax | awk "\$1 == $$ {print \$2}" )"
Run Code Online (Sandbox Code Playgroud)

有一种更快/更有效的方法,但它需要两个命令。

t=$(tty)
who|grep -wv "${t:5}"
Run Code Online (Sandbox Code Playgroud)

  • `ps斧头| grep "^ *$$"` 可能错误匹配,例如你的 shell 是 123 并且 1234 存在;`ps ax -otty= $$` 更健壮,而且只有一个进程。但我更喜欢你的 `${t:5}` 或 Stephane 的 `${t#/dev/}`(或 `substr(t,6)`) (2认同)

Sté*_*las 18

Zachary 已经解释了问题的根源。

虽然你可以解决它

tty=$(tty)
tty_without_dev=${tty#/dev/}
who | grep -v "$tty_without_dev"
Run Code Online (Sandbox Code Playgroud)

这将是错误的,例如,如果 tty 是pts/1,您最终会排除所有包含pts/10. 某些grep实现可以-w选择进行单词搜索

who | grep -vw pts/1
Run Code Online (Sandbox Code Playgroud)

将不匹配,pts/10因为pts/1in there 后面没有非单词字符。

或者您可以使用awk过滤第二个字段的确切值,例如:

who | awk -v "tty=$tty_without_dev" '$2 != tty'
Run Code Online (Sandbox Code Playgroud)

如果您想在一个命令中执行此操作:

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

原始标准输入被复制到文件描述符 3 并为tty命令恢复。

  • +1 用于弄清楚如何在一个命令中执行此操作并指出该错误。 (3认同)