Как сохранить только каждую n-ю строку файла

71

У меня довольно большой CSV-файл (75 МБ). Я просто пытаюсь составить график этого, поэтому мне действительно не нужны все данные.

Переписывание: я хотел бы удалить n строк, затем оставить одну строку, затем удалить n строк и так далее.

Так что если файл выглядел так:

Line 1
Line 2
Line 3
Line 4
Line 5
Line 6

и n = 2, тогда результат будет:

Line 3
Line 6

Кажется, что sedможет быть в состоянии сделать это, но я не смог понять, как. Команда bash была бы идеальной, но я открыт для любого решения.

Computerish
источник
2
Вы действительно хотите строки 1, 3, 6 и т. Д., А не 1, 4, 7 и т. Д.?
Илмари Каронен
2
Поскольку это CSV-файл, я предполагаю, что первая строка содержит метаданные (то есть имена полей). Если это так, вопрос должен быть «каждая n-ая строка после первой».
iglvzx
7
1, 3, 6 все еще не имеет смысла!
Вим
1
Я думаю, это должно быть 1, 3, 5, если n = 2 не является магическим значением для треугольных чисел (1, 3, 6, 10, 15, 21 и т. Д.)
rjmunro
4
Можете ли вы обновить свой вопрос, чтобы согласовать то, что вы запрашиваете («каждая n-я строка», «n = 2») и желаемый результат (строка 3, строка 6)? Будущие читатели будут сбиты с толку.
Кит Томпсон

Ответы:

121
~ $ awk 'NR == 1 || NR % 3 == 0' yourfile
Line 1
Line 3
Line 6

NRПеременная (количество записей) - это число записей, потому что поведение по умолчанию - новая строка для RS(разделитель записей). шаблон и действие необязательны в формате по умолчанию в awk 'pattern {actions}'. когда мы даем только часть шаблона, тогда awkзаписываются все поля $0для trueусловий нашего шаблона .

Сельман Улуг
источник
8
Благодаря настройкам по умолчанию вам даже не нужно так много:awk 'NR == 1 || NR % 3 == 0'
Кевин
@selman: Если вам нравится решение Кевина, возможно, вы захотите обновить свой ответ.
Кит Томпсон
4
Не хочешь объяснить, почему это так? Таким образом, если кто-то захочет немного подправить его, то, надеюсь, ваше объяснение поможет им в этом
Ivo Flipse
Я обнаружил, что при таком подходе строки 1 и 2 остаются нетронутыми. Это подтверждается тем, awk 'NR == 1 || NR % 2 == 0' myfile.txt | wc -lчто в результате получается нечетное число, тогда как в исходном файле было четное количество строк. Ответ @kev работает лучше всего в моем тестовом случае.
Даниэль Да Кунья
58

sed также можно сделать это:

$ sed -n '1p;0~3p' input.txt
Line 1
Line 3
Line 6

man sedобъясняет ~как:

первый шаг Шаг соответствует каждой строке шага, начиная с первой строки. Например, `` sed -n 1 ~ 2p '' напечатает все нечетные строки во входном потоке, а адрес 2 ~ 5 будет совпадать с каждой пятой строкой, начиная со второй. первым может быть ноль; в этом случае sed работает так, как если бы он был равен шагу. (Это расширение.)

кэв
источник
6
Не могли бы вы объяснить эту команду?
Qed
1
@qed Объяснение: 1pпечатает первую строку, 0~3pпечатает каждую третью строку, начиная со строки 3 ( 1pтаким образом, требуется для печати строки 1). Но обратите внимание, что 0~3это не стандартное, а расширение GNU sed.
Арку
«Это расширение». Какую версию вы использовали?
Виктор
Этот ответ мне очень помог для Windows PowerShell. Я расширил это так: sed -n '1p;0~10p' '.\in.txt' > out.txtнапечатать уменьшенный файл в выходной файл.
kimliv
22

Perl может сделать это тоже:

while (<>) {
    print  if $. % 3 == 1;
}

Эта программа напечатает первую строку своего ввода, а затем каждую третью строку.

Чтобы объяснить это немного, <>это оператор ввода строки, который перебирает строки ввода при использовании в whileцикле, подобном этому. Специальная переменная $.содержит количество прочитанных строк и %является оператором модуля.

Этот код можно записать еще более компактно , как однострочник, используя -nи -eпереключатели:

perl -ne 'print if $. % 3 == 1'  < input.txt  > output.txt

-eПереключатель принимает фрагмент кода Perl для выполнения в качестве параметра командной строки, в то время как -nпереключатель неявно оборачивает код в whileцикле , как это показано выше.


Редактировать: чтобы получить строки 1, 3, 6, 9, ... как в примере, а не строки 1, 4, 7, 10, ... как я впервые предположил, что вы хотели, замените $. % 3 == 1на $. == 1 or $. % 3 == 0.

Илмари Каронен
источник
7

Если вы хотите сделать это с помощью Bash- скрипта, вы можете попробовать:

#!/bin/sh

echo Please enter the file name
read fname
echo Please enter the Nth lines that you want to keep
read n

exec<$fname
value=0
while read line
do
    if [ $(( $value % $n )) -eq 0 ] ; then
        echo -e "$line" >> new_file.txt
    fi
        let value=value+1 
done
echo "Check the 'new_file.txt' that has been created in this directory";

Сохраните его как «read_lines.sh» и не забудьте дать + x разрешений файлу bash.

chmod +x ./read_lines.sh
akarpovsky
источник
1
Если вы сделали это просто с помощью стандартного вывода, прочитайте число строк, чтобы пропустить аргументы, и прочитайте файл из стандартного ввода, это будет проще и полезнее. Вы все еще можете создать new_file.txt, выполнив ./read_lines.sh > new_file.txt.
rjmunro
4

Решение в чистом bash, которое не порождает процесс:

{ for f in {1..2}; do read line; done;
  while read line; do
    echo $line;
    for f in {1..2}; do read line; done;
  done; } < file

Первая строка пропускает 2 строки в начале файла, а затем whileпечатает следующую строку и пропускает 2 строки снова.

Если ваш файл небольшой, это очень эффективный способ выполнить работу, так как он не запускает процесс. Когда ваш файл большой, его sedследует использовать, так как он более эффективен при обработке io, чем bash.

jfg956
источник
1

Версия Python (оба Python 2 и Python 3):

python2 -c "print(''.join(open('file.txt').readlines()[::3]))"

замените параметры [::3]начала, конца и размера шага для большего контроля. Например, [10:36:5]выдает строки 10,15, ..., 35.

Обратите внимание, что, поскольку readlines()строки заканчиваются, выходные данные этого вызова могут заканчиваться пустой последней строкой, если исходная последняя строка не будет выведена на выбранный размер шага.

Также возможна потоковая версия (здесь вывод только после законченного потока):

python -c "import sys;print(''.join(list(sys.stdin)[::3]))" < file.txt
DomTomCat
источник