Grep-Alias ​​- Zeilennummern, es sei denn, es befindet sich in einer Pipeline

25

Ich möchte einen Bash-Alias ​​für grep erstellen, der Zeilennummern hinzufügt:

alias grep='grep -n'

Aber das fügt natürlich auch den Pipelines Zeilennummern hinzu. Die meiste Zeit (und es fallen mir keine Ausnahmen ein) möchte ich keine Zeilennummern in einer Pipeline haben (zumindest intern, wahrscheinlich in Ordnung, wenn es die letzte ist), und ich möchte nicht wirklich ein sed / awk / cut hinzufügen die Pipeline nur, um sie herauszunehmen.

Vielleicht könnten meine Anforderungen vereinfacht werden, um "nur Zeilennummern hinzuzufügen, wenn grep der einzige Befehl in der Zeile ist". Gibt es eine Möglichkeit, dies ohne einen besonders hässlichen Alias ​​zu tun?

Kevin
quelle

Antworten:

27

Sie können eine Funktion in bash (oder einer beliebigen POSIX-Shell) wie folgt verwenden:

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

Das [ -t 1 ]Teil verwendet den [Befehl (auch bekannt als test) , um zu überprüfen, ob stdout einem tty zugeordnet ist.

Das [ -t 0 ]prüft auch die Standardeingabe, da Sie angegeben haben, nur Zeilennummern hinzuzufügen, wenn dies grepder einzige Befehl in der Pipeline ist.

Enzotib
quelle
5
Führen Sie den Test durch, [[ -t 0 && -t 1 ]]wenn Sie nur Zeilennummern wünschen, wenn sowohl die Standardeingabe als auch die Standardausgabe an ein Terminal angeschlossen sind.
Gilles 'SO- hör auf böse zu sein'
3

(zur Vollständigkeit)

Während die Antwort von @enzotib höchstwahrscheinlich das ist, was Sie wollen, ist es nicht das, wonach Sie gefragt haben. [ -t 1 ]prüft, ob es sich bei dem Dateideskriptor um ein Endgerät handelt, nicht um eine andere Pipe (wie eine normale Datei, einen Socket, einen anderen Gerätetyp wie /dev/null...)

Der [Befehl hat keine Entsprechung -taußer für Pipes. Um den Dateityp zu ermitteln, der einem Dateideskriptor zugeordnet ist, müssen Sie den fstat()Systemaufruf für diesen ausführen . Dafür gibt es keinen Standardbefehl, aber einige Systeme oder Shells haben einen.

Mit 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
}

Oder mit zshund seinem eigenen statBuiltin (das ein paar Jahre älter ist als GNU), hier nur geladen als 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
}

Nun ein paar Anmerkungen:

Es sind nicht nur Shell- Pipelines , die Pipelines verwenden.

var=$(grep foo bar)

oder:

cmd <(grep foo bar)

oder:

coproc grep foo bar

laufen Sie auch grepmit seinem stdout, das zu einem Rohr geht.

ksh93Beachten Sie, dass Ihre Shell auf einigen Systemen Socket-Paare anstelle von Pipes in ihren Pipelines verwendet.

Stéphane Chazelas
quelle