Как выбрать определенные строки (n, n + 4, n + 8, n + 12 ...) из файла?

11

Входные данные:

1
hgh
h2b
h4h
2
ok
koko
lkopk
3
uh
ju
nfjvn
4

Ожидаемый результат:

1
2
3
4

Итак, мне нужно иметь только 1-е, 5-е, 9-е, 13-е значение файла в выходном файле. Как это сделать?

Велю
источник
2
см. также: unix.stackexchange.com/questions/325985/… с помощью GNU sed вы можете это сделатьsed -n '1~4p'
Sundeep
1
Связанный: superuser.com/questions/396536/…
Жюльен Лопес

Ответы:

28

Использование AWK:

awk '!((NR - 1) % 4)' input > output

Выяснение того, как это работает, оставлено в качестве упражнения для читателя.

Стивен Китт
источник
спасибо за этот короткий курс awk!
darxmurf
20
NR % 4 == 1было бы более разборчивым ИМО.
Стефан
12
Согласен @ Стефан; это, вероятно, сомнительно с моей стороны, но для потенциально домашних заданий я стараюсь немного
Стивен Китт
@StephenKitt запутывает ваши ответы? В самом деле? Это не место для этого.
данные
22

Использование split (GNU coreutils):

split -nr/1/4 input > output
  • -nгенерировать CHUNKSвыходные файлы

и CHUNKSкак

  • r/K/N использовать циклическое распределение и выводить только Kth of N на стандартный вывод без разделения строк / записей
Фредди
источник
1
Разум взорван. Ответы как это - то, почему я люблю этот SE. Благодарность!
user1717828
21

С GNU sed:

sed '1~4!d' < input > output

Со стандартом sed:

sed -n 'p;n;n;n' < input > output

С 1и 4в $nи $iпеременные:

sed "$n~$i!d" # GNU only
awk -v n="$n" -v i="$i" 'NR >= n && (NR % i) == (n % i)'
Стефан Шазелас
источник
7

Добавление обязательного Perl-решения:

perl -ne 'print if $. % 4 == 1' input > output
wurtel
источник
4

Версия на Python, просто для удовольствия:

with open('input.txt') as f:
    for i, line in enumerate(f.readlines()):
        if i%4 == 0:
            print(line.strip())
user1717828
источник
enumerate(f)должен быть в состоянии выполнять работу, потребляя меньше памяти
iruvar
@iruvar Это так здорово! Никогда не осознавал этого раньше; будет использовать в будущем. Не стесняйтесь редактировать это в этом ответе; Я не собираюсь поддерживать его оптимизациями, так как другие ответы Bash (особенно этот ) определенно помогут.
user1717828
Если вы собираетесь использовать readlines(следовательно, выкладывая весь файл в память), вы можете использовать, f.readlines()[::4]чтобы получить каждую четвертую строку. Так что вы можете использовать print(''.join(f.readlines()[::4])).
Ник Маттео
3

POSIX sed: этот метод использует posixly sed и поэтому может использоваться везде, или, по крайней мере, те sed, которые уважают posix.

 $ sed -ne '
   /\n/!{
    H;s/.*//;x
   }

   :loop
       $bdone
       N;s/\n/&/4
       tdone
   bloop

   :done
   s/.//;P
 ' input.file

Еще одна программная генерация кода sed для масштабируемости:

$ code=$(yes n | head -n 4 | paste -sd\; | sed s/n/p/)
$ sed -ne "$code" input.file

Perl: мы заполняем массив A до размера 4. Затем мы печатаем его первый элемент, а также очищаем массив.

$ perl -pe '
   $A[@A] = @A ? <> : $_ while @A < 4;
   $_ = (splice @A)[0];
' input.file
Ракеш Шарма
источник
1

Вызов с scriptname filename skip(4 в вашем случае). Он работает, вытягивая iterстроки из верхней части файла и затем выводя только последние. Затем он приросты iterпо skipsи повторы тех пор , пока значение iterне превышается linesв file.

#!/bin/bash
file="$1"
lines=`wc -l < "$file"`
skips="$2" || "4"
iter=1
while [ "$iter" -le "$lines" ]; do
 head "$file" -n $iter | tail -n 1
 iter=$(( $iter + $skips ))
done
Райан Грандж
источник
1

Чистый Баш:

mapfile -t lines < input
for (( i=0; i < ${#lines[@]}; i+=4 ))
do printf "%s\n" "${lines[$i]}"
done

mapfile - это встроенный в Bash 4 встроенный модуль, который считывает стандартный ввод в массив, названный здесь lines, по одной строке на запись. -tВариант полоски окончательного перевода строки.

Если вы хотите печатать каждую четвертую строку, начиная со строки 4, то вы можете сделать это в одной команде, используя mapfileопцию обратного вызова -C, которая запускает предоставленный код каждые столько строк с интервалом, заданным -c. Текущий индекс массива и следующая назначаемая строка передаются коду в качестве аргументов.

mapfile -t -c4 -C 'printf "%.0s%s\n"' < input

Это использует printfвстроенный; код формата %.0sподавляет первый аргумент (индекс), поэтому печатается только строка.

Вы можете использовать ту же команду, чтобы печатать каждую четвертую строку, начиная со строки 1, 2 или 3, но вам нужно будет добавить 3, 2 или 1 строку перед inputтем, как вводить ее mapfile, что, я думаю, доставляет больше хлопот, чем стоит ,

Это также работает:

mapfile -t lines < input
printf "%s%.0s%.0s%.0s\n" "${lines[@]}"

Здесь используется printfчетыре элемента массива linesза раз, только печатая первую и пропуская остальные три с помощью %.0s. Мне это не нравится, так как вы должны вручную поиграть со строкой форматирования для разных интервалов или начальных точек.

Ник Маттео
источник