Як обрізати файл за рядками?


13

У мене є велика кількість файлів, деякі з яких дуже довгі. Я хотів би обрізати їх до певного розміру, якщо вони будуть більшими, видаливши кінець файлу. Але я хочу лише видалити цілі рядки. Як я можу це зробити? Схоже на те, що оброблятиме ланцюжок інструментів Linux, але я не знаю правильної команди.

Наприклад, скажіть, що у мене є 120 000-байтний файл з 300-байтовими рядками, і я намагаюся усікати його до 10 000 байт. Перші 33 рядки повинні залишитися (9900 байт), а решту слід скоротити. Я не хочу різати рівно 10 000 байт, оскільки це залишить частковий рядок.

Звичайно файли різної довжини, а рядки не однакові.

В ідеалі отримані файли були б трохи коротшими, а не трохи довшими (якщо точка розриву знаходиться на довгій лінії), але це не надто важливо, якщо це буде простіше. Я хотів би, щоб зміни були внесені безпосередньо до файлів (ну, можливо, новий файл скопійований в іншому місці, оригінал видалений та новий файл переміщений, але це те саме з POV користувача). Рішення, яке перенаправляє дані до місця, а потім назад, пропонує можливість пошкодити файл, і я хотів би цього уникнути ...


Видалили свою відповідь ... Я думаю, що розмір файлу в байтах був не надто чітким, вибачте. Можливо, ви могли б відредагувати своє запитання та уточнити цю частину (наприклад, із прикладом)?
slhck

@ slhck: Вибачте, що ви втратили представник лише тому, що мені було незрозуміло ... дозвольте мені побачити, чи можу я це виправити.
Чарльз

Не хвилюйтесь, я повинен був просто запитати, вибачте :)
slhck

Відповіді:


1

У попередніх відповідях, якщо вони використовуються, можна уникнути складності sed/ wcскладності awk. Використовуючи приклад, наданий з ОП (показує повні рядки до 10000 байт):

awk '{i += (length() + 1); if (i <= 10000) print $ALL}' myfile.txt

Також показано повний рядок, що містить 10000-й байт, якщо цей байт не знаходиться в кінці рядка:

awk '{i += (length() + 1); print $ALL; if (i >= 10000) exit}' myfile.txt

Відповідь вище передбачає:

  1. Текстовий файл є термінатором рядків Unix ( \n). Для текстових файлів Dos / Windows ( \r\n) змініть length() + 1наlength() + 2
  2. Текстовий файл містить лише один байт. Якщо є багатобайтовий символ (наприклад, у середовищі unicode), встановіть середовище LC_CTYPE=Cдля інтерпретації на рівні байтів.

15

sedПідхід добре, але в циклі по всіх лініях не є. Якщо ви знаєте, скільки рядків ви хочете зберегти (для прикладу, я тут використовую 99), ви можете зробити це так:

sed -i '100,$ d' myfile.txt

Пояснення: sedце звичайний процесор вираження. З -iнаданою опцією , він обробляє файл безпосередньо ("вбудований") - замість того, щоб просто його читати і записувати результати на стандартний вихід. 100,$просто означає "від рядка 100 до кінця файлу" - за ним слідує команда d, за якою ви, мабуть, правильно здогадалися, щоб поступити на "видалити". Отже, коротше, команда означає: "Видаліть всі рядки від рядка 100 до кінця файла з myfile.txt". 100 - перший рядок, який потрібно видалити, оскільки ви хочете зберегти 99 рядків.

Редагувати: Якщо, з іншого боку, є файли журналів, де ви хочете зберегти, наприклад останні 100 рядків:

[ $(wc -l myfile.txt) -gt 100 ] && sed -i "1,$(($(wc -l myfile.txt|awk '{print $1}') - 100)) d" myfile.txt

Що тут відбувається:

  • [ $(wc -l myfile.txt) -gt 100 ]: зробіть наступне, лише якщо файл містить більше 100 рядків
  • $((100 - $(wc -l myfile.txt|awk '{print $1}'))): обчислити кількість рядків, які потрібно видалити (тобто всі рядки файлу, за винятком (останній) 100 для збереження)
  • 1, $((..)) d: видаліть усі рядки від першого до обчисленого рядка

EDIT: оскільки питання було щойно відредаговане, щоб дати більше деталей, я включу цю додаткову інформацію разом із моєю відповіддю. Додані факти:

  • певний розмір повинен залишатися у файлі (10 000 байт)
  • кожен рядок має певний розмір у байтах (300 байт у прикладі)

З цих даних можна обчислити кількість рядків, що залишиться як "/", що в прикладі означало б 33 рядки. Термін оболонки для обчислення: $((size_to_remain / linesize))(принаймні, в Linux, що використовує Bash, результат - ціле число). Коригувана команда тепер буде читати:

# keep the start of the file (OPs question)
sed -i '34,$ d' myfile.txt
# keep the end of the file (my second example)
[ $(wc -l myfile.txt) -gt 33 ] && sed -i "1,33 d" myfile.txt

Оскільки розміри відомі заздалегідь, більше немає потреби в обчисленні, вбудованому в sedкоманду. Але для гнучкості всередині сценарію оболонки можна використовувати змінні.

Для умовної обробки, що базується на розмірі файлу, можна використовувати наступний "test" -konstruct:

[ "$(ls -lk $file | awk ' {print $5}')" -gt 100 ] &&

що означає: "якщо розмір $fileперевищує 100 кБ, зробіть ..." ( ls -lkперераховує розмір файлу в кБ у позиції 5, отже awk, використовується для вилучення саме цього).


ОП хоче вирізати файл на основі певного розміру байтів - а не лише довжини з точки зору рядків. Я видалив свою відповідь, що стосується head -n.
slhck

@slhck Дякую за повідомлення Так, ОП лише відредагувало його питання, щоб зробити наміри більш зрозумілими. Оскільки він має намір обчислити, скільки байтів має кожен рядок, моя відповідь залишається в принципі вірною - оскільки він може обчислити кількість рядків, що залишиться, а потім використовувати мій підхід для обробки файлів. Можливо, я коротко зауважу про це у своїй відповіді.
Іззі

Ні - розміри не відомі заздалегідь. Це був приклад. Кожен файл матиме різний розмір, а рядки мають неправильну довжину. Деякі файли взагалі не потрібно усікати.
Чарльз

О, знову ж таки ... Ну, деякі речі важко чітко пояснити (занадто багато граней). Що стосується файлів, які не потребують скорочення, це, мабуть, ґрунтується на розмірі файлу? Це можна прикрити. Але якщо не відомий навіть середній розмір рядка, ця частина стає важкою - наразі я не можу придумати просте рішення (без занадто великих накладних витрат).
Іззі

Все, що я можу придумати, включало б, наприклад, отримати перші n рядків, обчислити середню довжину на їх основі та використати це значення. Чи допоможе вам це?
Іззі

0

Не вдавшись знайти команду для цього, я написав швидкий сценарій (не перевірений):

#!/bin/sh

# Usage: $0 glob.* 25000
# where glob.* is a wildcard pattern and 25000 is the maximum number of bytes.

limit=20000
tmp=/tmp/trim
[[ "$2" == +([0-9]) ]] || limit=$2
limit=`expr $len + 1`
for file in $1;
do
    [[ `wc -c $file` -lt $limit ]] && continue
    head -c $file > $tmp
    sed '$d' $tmp
    $tmp > $file
done

-1

Ви можете використовувати команду linux sed для видалення рядків з файлу. Наступна команда видаляє останній рядок імені файлу.txt:

sed '$d' filename.txt

За допомогою awk або пошуку ви можете шукати шаблон, що відповідає вашій команді sed. Спочатку ви шукаєте з awk або знаходите файли, які ви хочете скоротити, а потім можете видалити рядки за допомогою sed.


-1

Я зробив щось подібне з хвостом. Щоб зберегти в цьому випадку лише останні 10000 рядків:

TMP=$(tail -n 10000 /path/to/some/file 2>/dev/null) && echo "${TMP}" > /path/to/some/file
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.