Alias ​​Grep - números de linha, a menos que esteja em um pipeline

25

Eu quero criar um alias do bash para grep que adicione números de linha:

alias grep='grep -n'

Mas isso, é claro, adiciona números de linhas aos pipelines também. Na maioria das vezes (e não há exceções), não quero números de linhas dentro de um pipeline (pelo menos internamente, provavelmente está bom se for o último) e não quero adicionar realmente um sed / awk / cut para o gasoduto apenas para retirá-los.

Talvez meus requisitos possam ser simplificados para "apenas adicionar números de linha se grep for o único comando na linha". Existe alguma maneira de fazer isso sem um pseudônimo particularmente feio?

Kevin
fonte

Respostas:

27

Você pode usar uma função no bash (ou em qualquer shell POSIX) como este:

grep() { 
    if [ -t 1 ] && [ -t 0 ]; then 
        command grep -n "$@"
    else 
        command grep "$@"
    fi
}

A [ -t 1 ]parte usa o [comando (também conhecido como test) para verificar se stdout está associado a um tty.

A [ -t 0 ]verificação também verifica a entrada padrão, pois você especificou apenas a adição de números de linha se esse grepfor o único comando no pipeline.

enzotib
fonte
5
E faça o teste [[ -t 0 && -t 1 ]]se você quiser apenas números de linha se a entrada e a saída padrão estiverem conectadas a um terminal.
Gilles 'SO- stop be evil'
3

(para completar)

Embora a resposta do @ enzotib seja provavelmente o que você deseja, não é o que você pediu. [ -t 1 ]verifica se o descritor de arquivo é um dispositivo terminal, não se trata de algo que não seja um pipe (como um arquivo comum, um soquete, outro tipo de dispositivo como /dev/null...)

O [comando não tem equivalente a não -tser para pipes. Para obter o tipo de arquivo associado a um descritor de arquivo, você precisa executar a fstat()chamada do sistema. Não existe um comando padrão para fazer isso, mas alguns sistemas ou shells possuem.

Com o 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
}

Ou com zshe seu próprio statbuiltin (que antecede o GNU um por alguns anos), aqui carregado como zstatapenas:

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
}

Agora, algumas notas:

Não são apenas os oleodutos que usam tubos.

var=$(grep foo bar)

ou:

cmd <(grep foo bar)

ou:

coproc grep foo bar

também corra grepcom seu stdout indo para um cano.

Se o seu shell for ksh93, observe que em alguns sistemas, ele usa pares de soquetes em vez de tubos em seus pipelines.

Stéphane Chazelas
fonte