web-dev-qa-db-fra.com

Comment trouver une chaîne de plusieurs lignes dans un script shell?

Je veux trouver la ficelle

Time series prediction with ensemble models

j'utilise pdftotext "$file" - | grep "$string". Où $file est le nom du fichier pdf et $string est la chaîne ci-dessus.Il peut trouver la ligne si la chaîne entière contient dans une ligne, mais elle ne peut pas trouver la ligne comme:

Time series prediction with 
ensemble models

comment puis-je le résoudre.Je suis nouveau sur linux. donc, l'explication détaillée est appréciée.Merci d'avance.

4
Mousumi

Une solution possible pourrait consister à remplacer grep par pcregrep (disponible dans le référentiel 'universe'), qui prend en charge les correspondances sur plusieurs lignes, puis au lieu de rechercher la chaîne littérale.

Time series prediction with ensemble models

recherchez plutôt expression régulière compatible Perl (PCRE)

Time\s+series\s+prediction\s+with\s+ensemble\s+models

\s+ représente un ou plusieurs caractères d'espacement (incluant les nouvelles lignes). Utilisation des fonctionnalités intégrées de substitution de chaînes du shell bash pour effectuer la dernière étape

pdftotext "$file" - | pcregrep -M "${string// /\\s+}"

Si vous ne pouvez pas utiliser pcregrep, vous pourrez peut-être obtenir le résultat souhaité avec plain grep avec le commutateur -z: cela indique à grep de prendre en compte l'entrée " "lignes" à délimiter par NUL caractères plutôt que par des nouvelles lignes - dans ce cas, cela permet de traiter efficacement l’entrée entière comme une seule ligne. Donc, par exemple, si vous voulez seulement imprimer les correspondances (sans contexte)

pdftotext "$file" - | grep -zPo "${string// /\\s+}"
4
steeldriver

Avec Python, beaucoup peut être fait ...

Si je le relise plus tard, je pourrai probablement faire une optimisation, mais lors de mes tests, le script ci-dessous fait le travail.

Testé sur un fichier:

Monkey eats banana since he ran out of peanuts 
Monkey
eats banana since he ran 
out of peanuts 
really, Monkey eats banana since 
he ran out of peanuts 
A lot of useless text here…
Have to add some lines for the sake of the test.
Monkey eats banana since he ran out of peanuts 

à la recherche d'une chaîne de caractères "Le singe mange de la banane puisqu'il n'a plus d'arachides", il affiche:

Found matches
--------------------
[line 1]
Monkey eats banana since he ran out of peanuts
[line 2]
Monkey
eats banana since he ran
out of peanuts
[line 5]
Monkey eats banana since
he ran out of peanuts
[line 9]
Monkey eats banana since he ran out of peanuts

Le scénario

#!/usr/bin/env python3
import subprocess
import sys

f = sys.argv[1]; string = sys.argv[2]

# convert to .txt with your suggestion
subprocess.call(["pdftotext", f])
# read the converted file
text = open(f.replace(".pdf", ".txt")).read()
# editing the file a bit for searching options / define th length of the searched string
subtext = text.replace("\n", " "); size = len(string)
# in a while loop, find the matching string and set the last found index as a start for the next match
matches = []; start = 0
while True:
    match = subtext.find(string, start)
    if match == -1:
        break
    else:
        matches.append(match)
    start = match+1

print("Found matches\n"+20*"-")
for m in matches:
    # print the found matches, replacing the edited- in spaces by (possibly) original \n
    print("[line "+str(text[:m].count("\n")+1)+"]\n"+text[m:m+size].strip())

Pour l'utiliser:

  1. copiez le script dans un fichier vide, enregistrez-le sous le nom search_pdf.py
  2. Exécutez-le à l'aide de la commande:

    python3 /path/to/search_pdf.py /path/to/file.pdf string_to_look_for
    

Inutile de mentionner que vous devez utiliser des guillemets si le chemin ou la chaîne recherchée contient des espaces:

python3 '/path to/search_pdf.py' '/path to/file.pdf' 'string to look for'
1
Jacob Vlijm

Une autre approche suggérée par steeldriver dans les commentaires consiste à remplacer tous les sauts de ligne par des espaces, en convertissant la sortie de pdftotext en une longue ligne et en recherchant que:

string="Time series prediction with ensemble models"
pdftotext "$file" - | tr '\n' ' ' | grep -o "$string"

J'ai ajouté le -o pour que grep imprime uniquement la partie correspondante de la ligne. Sans cela, vous obtiendrez tout le contenu du fichier imprimé.


Une autre approche consisterait à utiliser le commutateur -z de grep qui lui dit d'utiliser \0 au lieu de \n pour définir des lignes. Cela signifie que toute l'entrée sera traitée comme une "ligne" unique et que vous pouvez utiliser des expressions régulières compatibles Perl ou étendues pour la faire correspondre:

$ printf 'foo\nbar\nbaz\n' | grep -oPz 'foo\nbar'
foo
bar

Ceci n’aidera cependant pas à moins que vous sachiez au préalable comment la chaîne a été divisée en plusieurs lignes.

0
terdon