Я надеюсь производить символы между двумя положениями A и B, которые указаны предыдущей строкой. На пару эти две строки равны по длине, но среди пар могут варьироваться длины. Есть ли эффективный путь (огромные размеры файла), чтобы сделать это с grep
, sed
, или awk
?
Файл в качестве примера:
xxxxxxAxxxxxxBxxxxxx
1234567890MNOPQRSTUV
xxAxxxxxxxxxxxxxxBxxxxxx
1234567890MNOPQRSTUVWXYZ
...
Я хотел бы получить вывод:
7890MNOP
34567890MNOPQRST
...
Используя awk
:
$ awk '!seen{match($0, /A.*B/);seen=1;next} {print substr($0,RSTART,RLENGTH);seen=0}' infile
7890MNOP
34567890MNOPQRST
Объяснение: читайте в человеке awk
:
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.
Так как Вы упомянули sed, можно сделать это с 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
}
При помещении этого всего на одну командную строку она похожа на это:
$ 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
необходим так, чтобы sed
может понять regex группирующиеся круглые скобки без дополнительных Escape.
FWIW, я не думаю, что это могло быть сделано просто с grep
, хотя я был бы рад быть доказанным неправым.
Хотя можно сделать это с AWK, я предлагаю Perl. Вот сценарий:
#!/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";
}
Можно назвать тот файл сценария вообще, Вам нравится. Если необходимо было назвать его interval
и вставленный в текущий каталог, затем можно отметить его исполняемый файл с chmod +x interval
. Затем можно работать:
./interval paths...
Замена paths...
с фактическим путем или путями в файлы Вы хотите проанализировать. Например:
$ ./interval interval-example.txt
7890MNOP
34567890MNOPQRST
Способ, которым работает сценарий, состоит в том, что, пока конец входа не достигнут (т.е. больше никаких строк), он:
$pattern
, который является Вашей строкой с A
и B
, и другая строка, $text
, который является строкой, которая будет нарезана.A
в $pattern
и первое B
кроме любого, который, возможно, предшествовал этому сначала A
, и хранит их в $start
и $stop
переменные, соответственно.$text
чьи индексы располагаются от $start
кому: $stop
. Perl substr
функция берет смещение и аргументы длины, который является причиной вычитания, и Вы включаете букву сразу под B
, который является причиной добавления 1
.Если по некоторым причинам Вы предпочли бы короткую короткую команду, которая достигает того же самого, но легко вставляется в - но также и более трудна понять и поддержать - затем Вы могли использовать это:
perl -wple '$i=index $_,"A"; $_=substr <>,$i,index($_,"B",$i)-$i+1' paths...
(Как прежде, необходимо заменить paths...
с фактическими путями.)
Вот способ сделать это в GNU awk:
$ gawk 'NR%2 {split($0,a,/[AB]/); FIELDWIDTHS = length(a[1])" "length(a[2])+2; next} {print $2}' file
7890MNOP
34567890MNOPQRST
x
по определениюx
Поймать все эти ситуации, с помощью set()
, мы можем искать строки, которые только существуют (весь из) x
, A
, B
. Они, мы можем быть положительными, являются первыми строками наших пар.
Таким образом мы входим в 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
Таким образом, вывод:
Some results of whatever test
-----------------------------
xxxxxxAxxxxxxBxxxxxx
1234567890MNOPQRSTUV
blub or blublub
xxAxxxxxxxxxxxxxxBxxxxxx
1234567890MNOPQRSTUVWXYZ
peanutbutter
AxxxxxxxxxxxxxxBxxxxxx
x234567890MNOPQRSTUVWXYZ
становится:
7890MNOP
34567890MNOPQRST
x234567890MNOPQR
С очень простым синтаксисом Python 3 мы можем сделать следующий сценарий:
#!/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])
Который работает так:
$ ./croplines.py input.txt
7890MNOP
34567890MNOPQRST
OP обеспечил MCVE, но не обеспечил другие требования, таким образом, на основе того, что они показывают, у нас есть переменный шаблон: первая строка, которая запускается с "x", затем строка с данными (в этом случае числовой, но это не имеет значения для нашей цели).
Преимущества этого подхода:
for fname in sys.argv[1:]
, и мы могли даже добавить дополнительную гибкость определения шаблонов на командной строке;os.walk
модуль, если мы хотим/нуждаемсяfd.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])