![Grep 别名 - 行号,除非它在管道中](https://linux22.com/image/19223/Grep%20%E5%88%AB%E5%90%8D%20-%20%E8%A1%8C%E5%8F%B7%EF%BC%8C%E9%99%A4%E9%9D%9E%E5%AE%83%E5%9C%A8%E7%AE%A1%E9%81%93%E4%B8%AD.png)
我想为 grep 创建一个 bash 别名来添加行号:
alias grep='grep -n'
当然,这也会增加管道的行数。大多数时候(没有例外)我不希望管道内有行号(至少在内部,如果是最后一个可能没问题),而且我真的不想添加 sed/awk/cut管道只是为了把它们取出来。
也许我的要求可以简化为“如果 grep 是该行上唯一的命令,则仅添加行号”。有什么办法可以做到这一点而不需要一个特别难看的别名吗?
答案1
您可以在 bash(或任何 POSIX shell)中使用函数,如下所示:
grep() {
if [ -t 1 ] && [ -t 0 ]; then
command grep -n "$@"
else
command grep "$@"
fi
}
该[ -t 1 ]
部分使用[
命令(也称为test
)检查 stdout 是否与 tty 关联。
它[ -t 0 ]
还会检查标准输入,因为您指定仅添加行号(如果grep
是)仅有的管道中的命令。
答案2
(为了完整性)
尽管@enzotib 的回答很可能是您想要的,而不是您要求的。[ -t 1 ]
检查文件描述符是否是终端设备,而不是管道以外的任何设备(如常规文件、套接字、其他类型的设备,例如/dev/null
......)
该[
命令没有-t
与管道等效的命令。要获取与文件描述符关联的文件类型,您需要fstat()
对其执行系统调用。没有标准命令可以执行此操作,但某些系统或 shell 有一些命令。
使用 GNU stat
:
grep() {
if { [ "$(LC_ALL=C stat -c %F - <&3)" = fifo ]; } 3>&1 ||
[ "$(LC_ALL=C stat -c %F -)" = fifo ]; then
command grep "$@"
else
command grep -n "$@"
fi
}
或者使用zsh
它自己的stat
内置函数(比 GNU 早几年),这里仅加载zstat
:
grep() {
zmodload -F zsh/stat b:zstat
local stdin_type stdout_type
if zstat -A stdin_type -s -f 0 +mode &&
zstat -A stdout_type -s -f 1 +mode &&
[[ $stdin_type = p* || $stdout_type = p* ]]
then
command grep "$@"
else
command grep -n "$@"
fi
}
现在有一些注意事项:
它不仅仅是外壳管道使用管道的。
var=$(grep foo bar)
或者:
cmd <(grep foo bar)
或者:
coproc grep foo bar
也grep
将其标准输出运行到管道。
如果您的 shell 是ksh93
,请注意,在某些系统上,它在其管道中使用套接字对而不是管道。