Як зберегти лише кожен 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 була б ідеальною, але я відкритий для будь-якого рішення.


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
Хочете пояснити, чому це так? Таким чином, якщо хтось хоче це трохи підправити, то, сподіваємось, ваше пояснення допоможе їм зробити це
Іво Фліпс

Я виявив, що такий підхід залишає недоторканими лінії 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це не стандартне, а розширення sed GNU.
Арку

"Це розширення." Яку версію ви / використовували?
Віктор

Ця відповідь мені дуже допомогла для Windows PowerShell. Я розширив це так: sed -n '1p;0~10p' '.\in.txt' > out.txtнадрукувати зменшений файл у вихідний файл.
кімлів

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

1
Якщо ви це просто випромінюєте в стандартному режимі, прочитайте номер рядків, щоб пропустити аргументи, і прочитайте файл зі стандарту в, це було б простіше і корисніше. Ви все ще можете зробити new_file.txt, зробивши це ./read_lines.sh > new_file.txt.
rjmunro

4

Рішення в чистому стилі, що не породить процес, це:

{ 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.


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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.