Как обрезать файл по строкам?

13

У меня есть большое количество файлов, некоторые из которых очень длинные. Я хотел бы обрезать их до определенного размера, если они больше, удалив конец файла. Но я хочу только удалить целые строки. Как я могу это сделать? Это похоже на то, что будет обрабатываться инструментарием Linux, но я не знаю правильной команды.

Например, скажем, у меня есть файл размером 120000 байт с 300-байтовыми строками, и я пытаюсь обрезать его до 10000 байт. Первые 33 строки должны остаться (9900 байт), а остальные должны быть обрезаны. Я не хочу точно вырезать 10 000 байт, так как это оставит частичную строку.

Конечно, файлы имеют разную длину, а строки имеют разную длину.

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

Чарльз
источник
Удален мой ответ ... Я думаю, размер файла в байтах был не слишком ясен, извините. Может быть, вы могли бы отредактировать свой вопрос и уточнить эту часть (например, с примером)?
Slhck
@slhck: Извините, что вы потеряли представителя только потому, что мне было неясно ... позвольте мне посмотреть, смогу ли я это исправить.
Чарльз
Не беспокойтесь, я должен был просто спросить, извините :)
slhck

Ответы:

1

sed/ wcСложность можно избежать в предыдущих ответах , если awkиспользуется. Используя пример, предоставленный из OP (показаны полные строки до 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принудительную интерпретацию на уровне байтов.
Абель Чунг
источник
14

sedПодход хорошо, но в цикле по всем линиям не является. Если вы знаете, сколько строк вы хотите сохранить (для примера, я использую здесь 99), вы можете сделать это следующим образом:

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

Пояснение: sedэто процессор регулярных выражений. С указанным параметром -iон обрабатывает файл напрямую («inline»), а не просто читает его и записывает результаты в стандартный вывод. 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: удалить все строки от первой до расчетной

РЕДАКТИРОВАТЬ: так как вопрос был только что отредактирован, чтобы дать больше деталей, я включу эту дополнительную информацию вместе с моим ответом. Добавлены факты:

  • конкретный размер должен оставаться в файле (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команду. Но для большей гибкости внутри некоторого сценария оболочки можно использовать переменные.

Для условной обработки, основанной на размере файла, можно использовать следующую «тестовую» конструкцию:

[ "$(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 для удаления строк из файла. Следующая команда удаляет последнюю строку filename.txt:

sed '$d' filename.txt

С помощью awk или find вы можете искать шаблон, соответствующий вашей команде sed. Сначала вы ищете с помощью awk или находите файлы, которые хотите сократить, а затем вы можете удалить строки с помощью sed.

kockiren
источник
-1

Я сделал что-то похожее с хвостом. Чтобы сохранить только последние 10000 строк в этом случае:

TMP=$(tail -n 10000 /path/to/some/file 2>/dev/null) && echo "${TMP}" > /path/to/some/file
Билл М
источник