У меня есть большой файл, который выглядит так:
esup_255_3 transdecoder 7655 8192
esup_6093_1 transdecoder 2732 2774
esup_25727_1 transdecoder 1 60
...
со столбцами 3 и 4, представляющими интервалы чисел.
Я пытаюсь изменить этот файл, чтобы список чисел, содержащихся в интервалах, был указан в другом столбце (здесь, в столбце 5) следующим образом:
esup_255_3 transdecoder 7655 8192 7655
esup_255_3 transdecoder 7655 8192 7656
esup_255_3 transdecoder 7655 8192 7657
esup_255_3 transdecoder 7655 8192 ...
esup_255_3 transdecoder 7655 8192 8192
esup_6093_1 transdecoder 2732 2774 2732
esup_6093_1 transdecoder 2732 2774 2733
esup_6093_1 transdecoder 2732 2774 ....
esup_6093_1 transdecoder 2732 2774 2774
... and so on...
Я думаю, что Perl может помочь в этом, но я очень новичок в этом. Я владею только bash, и здесь я не могу найти правильный способ получить то, что мне нужно.
Что-то вроде этого?
perl -lne 'my ($line, $from, $to) = /^(.*\s(\d+)\s+(\d+)\s*)$/; print "$line\t$_" for $from..$to;'
Когда я запускаю его на вашем фрагменте, он печатает 641 строку:
esup_255_3 transdecoder 7655 8192 7655
esup_255_3 transdecoder 7655 8192 7656
esup_255_3 transdecoder 7655 8192 7657
[...]
esup_255_3 transdecoder 7655 8192 8190
esup_255_3 transdecoder 7655 8192 8191
esup_255_3 transdecoder 7655 8192 8192
esup_6093_1 transdecoder 2732 2774 2732
esup_6093_1 transdecoder 2732 2774 2733
[...]
esup_6093_1 transdecoder 2732 2774 2773
esup_6093_1 transdecoder 2732 2774 2774
esup_25727_1 transdecoder 1 60 1
esup_25727_1 transdecoder 1 60 2
[...]
esup_25727_1 transdecoder 1 60 59
esup_25727_1 transdecoder 1 60 60
Далее следует объяснение. Начнем с вариантов:
perl -lne
Мы возьмем их справа налево. -e
(для «выполнить» или «оценить») просто сообщает Perl, что следующее в командной строке — это код для запуска, поэтому он не будет искать код на стандартном вводе.
-n
говорит ему автоматически перебирать ввод построчно; он действует так, как будто вокруг фактического кода обернут цикл while (<>) {
...}
. Внутри тела цикла текущая строка будет найдена в переменной темы $_
.
-l
говорит ему удалить символы новой строки из ввода и автоматически добавить их к каждой распечатываемой строке; это в основном убирает новые строки из картины и упрощает логику.
Таким образом, программа будет читать ввод построчно и запускать код, указанный в качестве аргумента для -e
, в каждой строке. Давайте посмотрим на этот код, который начинается с этого утверждения:
my ($line, $from, $to) = /^(.*\s(\d+)\s+(\d+)\s*)$/;
Регулярное выражение не имеет явной строки для сопоставления, поэтому оно автоматически сопоставляется с $_
, имеющей текущую строку. Он должен соответствовать всей строке (из-за ^
в начале и $
в конце). Фактическое значение строки также захватывается из-за крайних круглых скобок, поэтому это будет первый элемент, возвращаемый совпадением, который назначается переменной $line
.
Первая часть строки может быть любой (поскольку .*
соответствует всему), поэтому мы действительно смотрим на то, как заканчивается строка, а не на то, как она начинается. Первым интересным элементом является любой пробельный символ (\s
), который нужен, чтобы убедиться, что мы не пропустим ни одно из следующих чисел. В частности, мы ищем одну или несколько цифр (\d+
), которые заключаются в круглые скобки, поэтому это значение также будет возвращено при совпадении; это второй захват, поэтому он входит во вторую переменную в присваивании, $from
. После этих цифр мы ищем еще пробелы (требуется хотя бы один пробел, но допускается любое число), за которым следует еще одна последовательность цифр; этот второй набор цифр снова захватывается и возвращается, поэтому он оказывается в последней переменной $to
. Наконец, мы разрешаем за последним набором цифр следовать любое количество необязательных завершающих пробелов.
Итак, после прочтения вашей первой строки, $_ = "esup_255_3 transdecoder 7655 8192 "
, совпадение + присваивание установит $line
на копию всей этой строки, $from
на 7655
и $to
на 8192
.
Затем подходим к выходу. Эта строка:
print "$line\t$_" for $from .. $to;
Более короткий способ записи этого цикла:
foreach $_ ($from .. $to) {
print "$line\t$_";
}
Это означает, что он перебирает целые числа от $from
до $to
, повторно используя $_
в качестве управляющей переменной цикла (поэтому нам пришлось скопировать текущую строку в $line
). Для каждого значения в диапазоне выводится копия всей строки, за которой следует табуляция и текущий номер.