web-dev-qa-db-fra.com

Comment trouver des lignes contenant une chaîne, puis imprimer ces lignes spécifiques et autre chose

J'utilise la commande suivante pour rechercher récursivement plusieurs fichiers et trouver le numéro de ligne dans chaque fichier dans lequel se trouve la chaîne.

    grep -nr "the_string" /media/slowly/DATA/lots_of_files > output.txt

La sortie est la suivante:

    /media/slowly/DATA/lots_of_files/lots_of_files/file_3.txt:3:the_string
    /media/slowly/DATA/lots_of_files/lots_of_files/file_7.txt:6:the_string is in this sentence.
    /media/slowly/DATA/lots_of_files/lots_of_files/file_7.txt:9:the_string is in this sentence too.

Comme indiqué ci-dessus, la sortie inclut le nom de fichier, le numéro de ligne et tout le texte de cette ligne, y compris la chaîne.

J'ai également compris comment imprimer uniquement les lignes spécifiques d'un fichier contenant la chaîne à l'aide de la commande suivante:

    sed '3!d' /media/slowly/DATA/lots_of_files/lots_of_files/file_3.txt > print.txt
    sed '6!d' /media/slowly/DATA/lots_of_files/lots_of_files/file_7.txt >> print.txt
    sed '9!d' /media/slowly/DATA/lots_of_files/lots_of_files/file_7.txt >> print.txt

J'ai créé les commandes ci-dessus manuellement en lisant les numéros de ligne et les noms de fichiers

Voici ma question.

Q1a

Existe-t-il un moyen de combiner les deux étapes en une seule commande? Je pense à transférer le numéro de ligne et le nom de fichier dans sed et à imprimer la ligne. J'ai un problème avec l'ordre dans lequel la sortie grep est générée.

Q1b

Comme ci-dessus mais aussi imprimer les 2 lignes avant et 2 lignes après la ligne contenant la chaîne (total de 5 lignes)? Je pense à transférer le numéro de ligne et le nom de fichier dans sed et à imprimer toutes les lignes requises d'une manière ou d'une autre.

Grand merci.

8
speld_rwong

Si je comprends bien la question, vous pouvez le faire avec une seule commande grep.

Pour Q1a, votre sortie grep peut supprimer le nom de fichier à l'aide de -h, par exemple.:

grep -hnr "the_string" /media/slowly/DATA/lots_of_files > output.txt

Pour Q1b, votre sortie grep peut inclure des lignes précédant et suivant les lignes correspondantes à l'aide de -A et -B, par exemple.:

grep -hnr -A2 -B2 "the_string" /media/slowly/DATA/lots_of_files > output.txt

La sortie contiendra un séparateur entre les correspondances, que vous pouvez supprimer avec --no-group-separator, par exemple.:

grep -hnr -A2 -B2 --no-group-separator "the_string" /media/slowly/DATA/lots_of_files > output.txt

Notez que la sortie utilise un délimiteur différent pour les lignes correspondantes (:) et les lignes de contexte (-).

6
cherdt

Pour autant que je sache, vous pouvez répondre à votre première question en vous adressant à grep d'une manière différente. Lorsque vous lui envoyez une liste de fichiers (ou un répertoire à parcourir avec -r ou -R), il affichera toujours le fichier dans lequel il a trouvé une correspondance ainsi que le numéro de ligne. Vous pouvez contourner cela avec une construction telle que:

find /path/to/files -type f | xargs grep -n 'the_pattern'

Quant à votre deuxième question, si vous voulez voir les lignes avant et après une correspondance, vous pouvez utiliser le -C (pour C ontext) commutateur:

grep -C2 'pattern' /path/to/file # displays the two lines before and after a match

Relative à -C sont -A (pour A fter) et -B (pour B efore), qui ne donnent respectivement que le nombre spécifié de lignes après ou avant une correspondance.

Vous pouvez ainsi combiner les deux réponses:

find /path/to/files -type f | xargs grep -n -C2 'the_pattern'

Quant à votre question sur sed, l'exemple que vous avez donné ne fonctionne que si vous connaissez déjà les numéros de ligne. Vous pouvez également faire quelque chose comme:

sed -n '/the_pattern/p' /path/to/files/*

(mais il ne récursera pas dans les sous-répertoires)

0
DopeGhoti
find /media/slowly/DATA/lots_of_files -type f -exec grep -h -C2 'the_pattern' {} +

Cela trouvera des choses qui sont des fichiers (par opposition aux répertoires ou aux liens) dans le répertoire/media/slow/DATA/lots_of_files. Il les regroupera (pas besoin de xargs cette décennie) et exécutera grep sur eux. grep n'imprimera pas les noms de fichiers (-h) mais donnera 2 lignes de contexte avant et après les lignes correspondantes (-C2, utilisez -A et -B pour un contrôle plus précis).

L'avantage de cette commande par rapport à celle de @cherdt est que vous pouvez ajouter des filtres supplémentaires dans la commande find, par exemple, vous pouvez choisir de ne pas aller dans des répertoires comme .git

0
icarus