Grep 别名 - 行号,除非它在管道中

Grep 别名 - 行号,除非它在管道中

我想为 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,请注意,在某些系统上,它在其管道中使用套接字对而不是管道。

相关内容