Je cherche à sortir les caractères entre deux positions A et B qui sont spécifiées par la ligne précédente. Par paire, les deux lignes ont la même longueur, mais elles peuvent varier d'une paire à l'autre. Existe-t-il un moyen efficace (tailles de fichiers énormes) de le faire avec grep
name__, sed
ou awk
name__?
Exemple de fichier:
xxxxxxAxxxxxxBxxxxxx
1234567890MNOPQRSTUV
xxAxxxxxxxxxxxxxxBxxxxxx
1234567890MNOPQRSTUVWXYZ
...
Je voudrais obtenir le résultat:
7890MNOP
34567890MNOPQRST
...
Utilisation de awk
name__:
$ awk '!seen{match($0, /A.*B/);seen=1;next} {print substr($0,RSTART,RLENGTH);seen=0}' infile
7890MNOP
34567890MNOPQRST
Explication: lu dans man awk
name __ :
RSTART
The index of the first character matched by match(); 0 if no
match. (This implies that character indices start at one.)
RLENGTH
The length of the string matched by match(); -1 if no match.
match(s, r [, a])
Return the position in s where the regular expression r occurs,
or 0 if r is not present, and set the values of RSTART and RLENGTH. (...)
substr(s, i [, n])
Return the at most n-character substring of s starting at I.
If n is omitted, use the rest of s.
Bien que vous puissiez le faire avec AWK, je suggère Perl. Voici un script:
#!/usr/bin/env Perl
use strict;
use warnings;
while (my $pattern = <>) {
my $text = <>;
my $start = index $pattern, 'A';
my $stop = index $pattern, 'B', $start;
print substr($text, $start, $stop - $start + 1), "\n";
}
Vous pouvez nommer ce fichier de script comme bon vous semble. Si vous deviez le nommer interval
et le placer dans le répertoire en cours, vous pouvez le marquer comme exécutable avec chmod +x interval
. Ensuite, vous pouvez exécuter:
./interval paths...
Remplacez paths...
par le ou les chemins d'accès aux fichiers à analyser. Par exemple:
$ ./interval interval-example.txt
7890MNOP
34567890MNOPQRST
La façon dont le script fonctionne est la suivante: jusqu'à la fin de la saisie (c'est-à-dire plus de lignes), il:
$pattern
, qui est votre chaîne avec A
et B
name__, et une autre ligne, $text
, qui est la chaîne qui sera découpée en tranches.A
dans $pattern
et le premier B
de tous ceux ayant pu précéder le premier A
et les stocke dans les variables $start
et $stop
, respectivement.$text
dont les index vont de $start
à $stop
. La fonction substr
de Perl prend les arguments offset et length, ce qui est la raison de la soustraction, et vous incluez la lettre immédiatement sous B
name__, ce qui explique l'ajout de 1
.Si, pour une raison quelconque, vous préférez une commande courte sur une ligne, vous obtenez le même résultat mais vous le collez facilement - mais il est également plus difficile à comprendre et à gérer - vous pouvez alors utiliser ceci:
Perl -wple '$i=index $_,"A"; $_=substr <>,$i,index($_,"B",$i)-$i+1' paths...
(Comme auparavant, vous devez remplacer paths...
par les noms de chemin d'accès actuels.)
Puisque vous avez mentionné sed , vous pouvez également le faire avec un script sed:
/^x*Ax*Bx*$/{ # If an index line is matched, then
N # append the next (content) line into the pattern buffer
:a # label a
s/^x(.*\n).(.*)/\1\2/ # remove "x" from the index line start and a char from the content line start
ta # if a subtitution happened in the previous line then jump back to a
:b # label a
s/(.*)x(\n.*).$/\1\2/ # remove "x" from the index line end and a char from the content line end
tb # if a subtitution happened in the previous line then jump back to b
s/.*\n// # remove the index line
}
Si vous mettez tout cela sur une seule ligne de commande, cela ressemble à ceci:
$ sed -r '/^x*Ax*Bx*$/{N;:a;s/^x(.*\n).(.*)/\1\2/;ta;:b;s/(.*)x(\n.*).$/\1\2/;tb;s/.*\n//;}' example-file.txt
7890MNOP
34567890MNOPQRST
$
-r
est nécessaire pour que sed
puisse comprendre les parenthèses de regroupement des expressions rationnelles sans échappées supplémentaires.
FWIW, je ne pense pas que cela pourrait être fait uniquement avec grep
, bien que je serais heureux d’avoir tort.
x
par définitionx
name__Pour attraper toutes ces situations, en utilisant set()
, nous pouvons rechercher les lignes qui seulement existent (toutes) x
name__, A
name__, B
name__. Ceux-ci, nous pouvons être positifs, sont les premières lignes de nos couples.
Ainsi nous obtenons en python:
#!/usr/bin/env python3
f = "/path/to/file"
printresult = False
for l in open(f):
if printresult == True:
print(l[i[0]:i[1]])
printresult = False
Elif set(l.strip()) == {"A", "x", "B"}:
i = [l.index("A"), l.index("B") + 1]
printresult = True
Ainsi, la sortie de:
Some results of whatever test
-----------------------------
xxxxxxAxxxxxxBxxxxxx
1234567890MNOPQRSTUV
blub or blublub
xxAxxxxxxxxxxxxxxBxxxxxx
1234567890MNOPQRSTUVWXYZ
peanutbutter
AxxxxxxxxxxxxxxBxxxxxx
x234567890MNOPQRSTUVWXYZ
devient:
7890MNOP
34567890MNOPQRST
x234567890MNOPQR
Voici un moyen de le faire dans GNU awk:
$ gawk 'NR%2 {split($0,a,/[AB]/); FIELDWIDTHS = length(a[1])" "length(a[2])+2; next} {print $2}' file
7890MNOP
34567890MNOPQRST
Avec la très simple syntaxe Python 3, nous pouvons créer le script suivant:
#!/usr/bin/env python3
import sys
for fname in sys.argv[1:]:
with open(fname) as fd:
for line in fd:
if line.startswith('x'):
start_index = line.find('A')
end_index = line.rfind('B')
else:
print(line[start_index:end_index+1])
Qui fonctionne comme tel:
$ ./croplines.py input.txt
7890MNOP
34567890MNOPQRST
OP a fourni MCVE , mais n’a pas fourni d’autres exigences. Nous nous basons donc sur un schéma alternatif: première ligne commençant par "x", puis ligne avec les données (dans ce cas numérique, mais peu importe pour notre propos).
Les avantages de cette approche sont les suivants:
for fname in sys.argv[1:]
, et nous pourrions même ajouter une flexibilité supplémentaire en spécifiant des modèles sur la ligne de commande;os.walk
si nous voulons/devonsfd.readline()
#!/usr/bin/env python3
import sys
for fname in sys.argv[1:]:
with open(fname) as fd:
for line in fd:
start_index = 0
end_index = len(line)-1
if line.startswith('x'):
start_index = line.find('A')
end_index = line.rfind('B')+1
line = fd.readline()
print(line[start_index:end_index])