Convaincre grep de sortir toutes les lignes, pas seulement celles avec des correspondances

121

Disons que j'ai le fichier suivant:

$ cat test

test line 1
test line 2
line without the search word
another line without it
test line 3 with two test words
test line 4

Par défaut, grepchaque ligne contenant le terme recherché est renvoyée:

$ grep test test

test line 1
test line 2
test line 3 with two test words
test line 4

Si vous passez le --colorparamètre à, il grepmettra en surbrillance la partie de la ligne qui correspond à l'expression recherchée, mais ne renvoie toujours que les lignes contenant l'expression. Existe-t-il un moyen de grepgénérer chaque ligne du fichier source, mais de mettre en évidence les correspondances?

Mon terrible piratage actuel pour y parvenir (du moins sur les fichiers ne contenant pas plus de 10000 lignes consécutives sans correspondance) est le suivant:

$ grep -B 9999 -A 9999 test test

Capture d'écran des deux commandes

Si cela grepn’est pas possible, existe-t-il un autre outil de ligne de commande offrant les mêmes fonctionnalités? J'ai tripoté avec ack, mais il ne semble pas avoir une option pour cela non plus.

Michael Mrozek
la source
1
Doublure possible entre sites de: stackoverflow.com/questions/981601/… La réponse principale est la même pour les deux.
Ciro Santilli a annoncé le
1
Vous pouvez utiliser -C 9999à la place de -A 9999 -B 9999.Je fais toujours:grep -C 9999 pattern file
neo

Réponses:

184
grep --color -E "test|$" yourfile

Ce que nous faisons ici correspond au $motif et au motif de test. De toute évidence, il n’ya $rien à colorer, donc seul le motif de test obtient la couleur. Il -Esuffit d'activer la correspondance regex étendue.

Vous pouvez facilement créer une fonction comme ceci:

highlight () { grep --color -E "$1|$" "${@:1}" ; }
Jackson
la source
14
C'est flippant génial!
gvkv
3
Et en fonction: highlight () { grep --color -E "$1|$" $2 ; }. Utilisation:highlight test yourfile
Stefan Lasiewski
2
@StefanLasiewski: "$2"devrait également être cité.
Dennis Williamson
6
Mieux pourrait être: highlight () { grep --color -E "$1|$" "$@" }qui autorise les fichiers avec des espaces dans leurs noms et plusieurs fichiers.
Mike DeSimone
15
@ MikeDeSimone - Mais cela aura aussi "$1"dans les fichiers. Utilisationhighlight () { grep --color -E "$1|$" "${@:1}" }
Chris Down
40
ack --passthru --color string file

pour Ubuntu et Debian, utilisez ack-grep au lieu de ack

ack-grep --passthru --color string file
Dennis Williamson
la source
1
Oh, c'est bien évidemment dans la page de manuel Je suis aveugle. Merci
Michael Mrozek
le motif pour grap peut être spécifié explicitement avec --regexp string; L'équivalent ack / ack-grep est--match string
Rhubbarb le
Pour simuler ack --passthruavec perl:grep_passthrough () { ! perl -ne "print; \$e ||= /$@/; END{{exit \$e}}"; }
Lucas Cimon
ack --passthrufonctionne également sur les flux! Par exemple,ifconfig | ack --passthru inet
honkaboy
13

Une autre façon de le faire correctement et portably avec grep( en plus d' utiliser deux expressions rationnelles avec l' alternance comme dans la réponse acceptée) est par le motif vide (et chaîne respectivement null).
Il devrait fonctionner aussi bien avec les deux -Eet les -Fcommutateurs puisque, selon la norme :

-E
    Match using extended regular expressions. 
    [...] A null ERE shall match every line.

et

-F
    Match using fixed strings.
    [...] A null string shall match every line.

Donc, il s'agit simplement de courir

grep -E -e '' -e 'pattern' infile

et respectivement

grep -F -e '' -e 'string' infile
don_crissti
la source
1
Ou juste grep -F -e '' -e 'string'.
Stéphane Chazelas
Cela fonctionne pour moi avec GNU grep 2.25, busybox grep et celui des outils hérités.
Stéphane Chazelas
OK, mon mal, seq 3 | grep -F -e '' -e 2ne sort que 2 en 2.27 (et 2.26, mais pas 2.25, et pas dans la tête de git). Seulement avec -F(pas sans ou avec -E). IOW, seulement 2.26 et 2.27 semblent avoir le bogue et seulement avec -F.
Stéphane Chazelas
Notez que seq 3 | grep -F -e '' -e '' -e 2 cela semble fonctionner aussi bien avec 2.27
Stéphane Chazelas
1
La méthode donnée dans cette réponse est simple, correcte et semble assez rapide . Comme vous le dites , cela fonctionne automatiquement avec -F, ce qui évite d'avoir à s'inquiéter des caractères qui apparaissent dans les caractères string. Vous voudrez peut-être mentionner --color; Comme cette réponse flotte sur la page (vue par les votes), plus de personnes pourraient finir de la lire avant les autres réponses.
Eliah Kagan
11

J'ai la fonction suivante que j'utilise pour de telles choses:

highlight () {
    perl -pe "s/$1/\e[1;31;43m$&\e[0m/g"
}

En interne, ça a l'air un peu moche, c'est gentil et facile à utiliser, comme ça:

cat some_file.txt | highlight some_word

ou, pour un exemple un peu plus réel:

tail -f console.log | highlight ERROR

Vous pouvez modifier les couleurs comme bon vous semble (ce qui pourrait être difficile avec grep - je ne suis pas sûr) en modifiant les valeurs 1and 31et 43(after \e[) en valeurs différentes. Les codes à utiliser sont tous sur le lieu , mais voici une intro rapide: les 1 Bolds le texte, la 31rend rouge, et 43donne un fond jaune. 32ou 33serait différentes couleurs, ou 44ou 45seraient différents milieux: vous avez l'idée. Vous pouvez même le faire clignoter (avec un 5) si vous êtes si enclin.

Cela n'utilise pas de modules Perl spéciaux, et Perl est presque omniprésent, alors je m'attendrais à ce qu'il fonctionne à peu près n'importe où. La solution grep est très intelligente, mais le commutateur --color de grep n’est pas disponible partout. Par exemple, je viens d’essayer cette solution sur une machine Solaris exécutant bash et une autre machine exécutant ksh et ma machine Mac OS X locale exécutant zsh. Tout a bien fonctionné. Solaris a toutefois étouffé la grep --colorsolution.

De plus, ack est génial, et je le recommande à tous ceux qui ne l’ont pas encore découvert, mais j’ai eu quelques problèmes pour l’installer sur quelques-uns des nombreux serveurs sur lesquels je travaille. (J'oublie pourquoi: je pense que cela est lié aux modules Perl.)

Étant donné que je ne pense pas avoir déjà travaillé sur un système Unix sur lequel Perl n'est pas installé (à l'exception des systèmes de type intégré, des routeurs Linksys, etc.), je dirais que cette solution est utilisable de manière universelle. .

iconoclaste
la source
3

Au lieu d'utiliser Grep, vous pouvez utiliser Less:

less file

Rechercher comme ceci: /pattern Enter

Cette volonté:

  1. défiler jusqu'à la première ligne correspondante
  2. sortie chaque ligne à partir de là sur
  3. mettre en évidence tous les matchs

Pour faire défiler jusqu'à la ligne correspondante suivante: n

Pour faire défiler jusqu'à la ligne correspondante précédente: N

Pour activer la surbrillance: Esc u

Vous pouvez aussi changer la couleur de surbrillance si vous le souhaitez.

Steven Penny
la source
2

Tu peux essayer:

perl -MTERM::ANSIColor -nle '/pattern/ ? print colored($_, 'color') : print' test

Pas très portable cependant, et même si Perl est installé, vous devrez peut-être télécharger un autre module. De plus, il colorera toute la ligne, pas seulement le mot recherché.

gvkv
la source
2

ripgrep

Utiliser ripgrepavec son --passthruparamètre:

rg --passthru pattern file.txt

C'est l'un des outils de recherche les plus rapides , car il est construit sur le moteur de regex de Rust qui utilise des automates finis, SIMD et des optimisations littérales agressives pour rendre la recherche très rapide.

--passthru - Imprimez les lignes correspondantes et non correspondantes.

Un autre moyen d'obtenir un effet similaire consiste à modifier votre modèle afin qu'il corresponde à la chaîne vide. Par exemple, si vous effectuez une recherche, utilisez rg fooà la rg "^|foo"place émettra chaque ligne dans chaque fichier recherché, mais seules les occurrences de foo seront mises en surbrillance. Cet indicateur active le même comportement sans qu'il soit nécessaire de modifier le motif.

Kenorb
la source
1

Il existe un moyen beaucoup plus simple de faire cela pour GNU grep mais je ne pense pas que ce soit portable (c'est-à-dire BSD grep):

Dans une pipe:

cat <file> | grep --color=always -z <query>

Sur un fichier:

grep --color=always -z <query> <file>

Le mérite en revient à la réponse de Cyrus ici .

Erik Nomitch
la source
1
Ce n'est pas une aussi bonne réponse que vous (et Cyrus) pensez. Cela a pour effet de traiter le fichier entier comme une seule ligne. Par conséquent, (1) si le fichier est très volumineux, il peut y avoir une possibilité de manque de mémoire, et (2) si le fichier ne contient pas le motif du tout , rien ne sera généré. Et tu as raison ce n'est pas universellement disponible. (Mais encore une fois, ce --colorn'est pas la norme non plus.)
G-Man le
Sur quelle version de grep cette version est-elle supportée? Sur grep 2.5.4, -z ne semble pas disponible ...
Alex
1

Aucune des réponses fournies jusqu'à présent ne fournit une solution portable .

Voici un portable 1 fonction shell I déjà posté dans un endroit fermé en double question qui ne nécessite pas d' outils non standard ou extensions non standard fournis avec perl, ack, ggrep, gsed, bashet les goûts , mais n'a besoin que d' un shell POSIX et les services publics obligatoires POSIX sedet printf:

grepc()
{
  pattern=$1
  shift
  esc=$(printf "\033")
  sed 's"'"$pattern"'"'$esc'[32m&'$esc'[0m"g' "$@"
}

Vous pouvez l'utiliser de cette façon:

grepc string_to_search [file ...]

La couleur de surbrillance peut être ajustée en utilisant l’un des codes suivants dans l’argument de la commande sed (32m étant vert ici):

30m black
31m red
33m yellow
34m blue
35m magenta
36m cyan
37m white
7m reverse video

1 Tant que votre terminal prend en charge les séquences d'échappement de couleurs ANSI.

jlliagre
la source
0

Une sedversion, fonctionne à la fois bashet ash.

#highlight
hsed(){
    local pattern="$1"
    shift
    local r=`echo -e '\e'[31m`
    local c=`echo -e '\e'[0m`
    sed "s:${pattern//:/\:}:$r\0$c:g" "$@"
}
Yurenchen
la source
0

OP a demandé grep, et c'est ce que je recommande ; mais après avoir essayé de résoudre un problème avec sed, pour mémoire, voici une solution simple:

sed $'s/main/\E[31m&\E[0m/g' testt.c

ou

cat testt.c | sed $'s/main/\E[31m&\E[0m/g'

Va peindre mainen rouge.

  • \E[31m : séquence de démarrage de couleur rouge
  • \E[0m : marque de couleur finie
  • & : le motif assorti
  • /g : tous les mots en ligne, pas seulement le premier
  • $'string' est des chaînes bash avec des caractères d'échappement interprétés

En ce qui concerne grep , cela fonctionne également en utilisant ^(début de ligne) au lieu de $(fin de ligne). Exemple:

egrep "^|main" testt.c

Et juste pour montrer cet alias fou que je ne recommande pas , vous pouvez même laisser les guillemets ouverts:

alias h='egrep -e"^|'
h main" testt.c
cat testt.c | h main"

tout le travail! :) Ne vous inquiétez pas si vous oubliez de fermer la citation, bash se souviendra de vous avec un "caractère de ligne continue".

Dr Beco
la source