Как я могу извлечь предопределенный диапазон строк из текстового файла в Unix?

436

У меня есть дамп SQL ~ 23000 строк, содержащий несколько данных на базе данных. Мне нужно извлечь определенный раздел этого файла (т.е. Данные для одной базы данных) и поместить его в новый файл. Я знаю как начальную, так и конечную строки данных, которые я хочу.

Кто-нибудь знает команду Unix (или ряд команд), чтобы извлечь все строки из файла между строкой 16224 и 16482 и затем перенаправить их в новый файл?

  • 0
    Поскольку вы упоминаете большие файлы, я предлагаю проверить комментарий stackoverflow.com/questions/83329/…
Теги:
command-line
sed
text-processing

21 ответ

658
Лучший ответ
sed -n '16224,16482p;16483q' filename > newfile

Из руководства sed:

p - Распечатайте пространство шаблона (до стандартного вывода). Эта команда обычно используется только в сочетании с параметром командной строки -n.

n - Если автоматическая печать не отключена, распечатайте пространство шаблонов, а затем, независимо от того, замените пространство шаблонов на следующую строку ввода. Если ввода больше нет, то sed выходит без обработки каких-либо команд.

q - Выйти из sed без обработки каких-либо команд или ввода. Обратите внимание, что текущее пространство шаблонов печатается, если автоматическая печать не отключена с помощью опции -n.

а также

Адреса в сценарии sed могут быть в любой из следующих форм:

number Указание номера строки будет соответствовать только этой строке на входе.

Диапазон адресов можно указать, указав два адреса, разделенных запятой (,). Диапазон адресов соответствует строкам, начинающимся с того, где совпадает первый адрес, и продолжается до второго совпадения (включительно).

  • 1
    Мне было любопытно, если это изменяет оригинальный файл. Я сделал резервную копию на всякий случай, и, похоже, это НЕ изменило оригинал, как ожидалось.
  • 0
    @AndyGroff. Чтобы изменить файл на месте, используйте параметр "-i". В противном случае он не изменит файл.
Показать ещё 6 комментариев
173
sed -n '16224,16482 p' orig-data-file > new-file

Где 16224,16482 - номер стартовой линии и номер конечной линии, включительно. Это 1-индекс. -n подавляет эхо ввода как выход, чего вы явно не хотите; номера указывают диапазон линий, чтобы включить следующую команду; команда p выводит соответствующие строки.

  • 6
    Для больших файлов указанная выше команда продолжит просмотр всего файла после того, как будет найден нужный диапазон. Есть ли способ, чтобы sed прекратил обработку файла после вывода диапазона?
  • 37
    Итак, из ответа здесь sed -n '16224,16482p;16482q' orig-data-file > new-file что остановка в конце диапазона может быть выполнена с помощью: sed -n '16224,16482p;16482q' orig-data-file > new-file .
Показать ещё 2 комментария
72

Довольно просто использовать head/tail:

head -16482 in.sql | tail -258 > out.sql

с помощью sed:

sed -n '16482,16482p' in.sql > out.sql

с помощью awk:

awk 'NR>=10&&NR<=20' in.sql > out.sql
  • 1
    Второй и третий варианты в порядке, но первый медленнее, чем многие другие, потому что он использует 2 команды, где 1 достаточно. Это также требует вычисления, чтобы получить правильный аргумент для tail .
  • 3
    Стоит отметить, что для сохранения тех же номеров строк, что и в вопросе, команда sed должна быть sed -n 16224,16482p' in.sql >out.sql а команда awk должна быть awk 'NR>=16224&&NR<=16482' in.sql > out.sql
Показать ещё 4 комментария
22

Вы можете использовать "vi", а затем следующую команду:

:16224,16482w!/tmp/some-file

Альтернативно:

cat file | head -n 16482 | tail -n 258

EDIT: - Чтобы добавить объяснение, вы используете head -n 16482 для отображения первых 16482 строк, затем используйте tail -n 258, чтобы получить последние 258 строк из первый выход.

  • 6
    Файл head -n 16482 | tail -n 258 должен работать лучше
  • 2
    И вместо vi вы можете использовать ex, то есть vi без интерактивной консоли.
Показать ещё 2 комментария
19

Существует другой подход с awk:

awk 'NR==16224, NR==16482' file

Если файл огромен, после прочтения последней желаемой строки может быть полезно exit. Таким образом, он не будет излишне читать файл до конца:

awk 'NR==16224, NR==16482-1; NR==16482 {print; exit}' file
13
perl -ne 'print if 16224..16482' file.txt > new_file.txt
7
 # print section of file based on line numbers
 sed -n '16224 ,16482p'               # method 1
 sed '16224,16482!d'                 # method 2
5

sed -n '16224,16482p' < dump.sql

5
cat dump.txt | head -16224 | tail -258

должен сделать трюк. Недостатком этого подхода является то, что вам необходимо выполнить арифметику, чтобы определить аргумент для хвоста и учесть, хотите ли вы, чтобы "между" включал конечную строку или нет.

  • 4
    Вам не нужна команда cat ; head может читать файл напрямую. Это медленнее, чем многие альтернативы, потому что он использует 2 (3, как показано) команды, где 1 достаточно.
  • 0
    @JonathanLeffler Этот ответ легче всего читать и запоминать. Если бы вы действительно заботились о производительности, вы бы вообще не использовали оболочку. Хорошей практикой является позволить отдельным инструментам посвятить себя определенной задаче. Кроме того, «арифметика» может быть решена с помощью | tail -$((16482 - 16224)) .
3

Быстрая и грязная:

head -16428 < file.in | tail -259 > file.out

Вероятно, это не лучший способ сделать это, но он должен работать.

BTW: 259 = 16482-16224 + 1.

  • 0
    Это медленнее, чем многие альтернативы, потому что он использует 2 команды, где 1 достаточно.
2

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

ruby -ne 'puts "#{$.}: #{$_}" if $. >= 32613500 && $. <= 32614500' < GND.rdf > GND.extract.rdf
2

Даже мы можем это сделать, чтобы проверить в командной строке:

cat filename|sed 'n1,n2!d' > abc.txt

Пример:

cat foo.pl|sed '100,200!d' > abc.txt
  • 4
    Вам не нужна команда cat ни в одном из них; sed отлично способен читать файлы самостоятельно, или вы можете перенаправить стандартный ввод из файла.
2

Я написал программу Haskell под названием splitter, которая делает именно это: прочитайте мой пост в блоге.

Вы можете использовать программу следующим образом:

$ cat somefile | splitter 16224-16482

И это все, что есть. Вам понадобится Haskell, чтобы установить его. Просто:

$ cabal install splitter

И все готово. Я надеюсь, что вы найдете эту программу полезной.

  • 0
    splitter читает только со стандартного ввода? В некотором смысле это не имеет значения; команда cat является излишней, делает это или нет. Либо используйте splitter 16224-16482 < somefile либо (если он принимает аргументы имени файла) splitter 16224-16482 somefile .
2

Я бы использовал:

awk 'FNR >= 16224 && FNR <= 16482' my_file > extracted.txt

FNR содержит номер записи (строки) строки, считанной из файла.

2

Я собирался опубликовать трюк с головой/хвостом, но на самом деле я, вероятно, просто запустил emacs.; -)

  • esc - x goto-line ret 16224
  • mark (ctrl - space)
  • esc - x goto-line ret 16482
  • esc - w

открыть новый выходной файл, ctl-y сохранить

Позвольте мне посмотреть, что происходит.

  • 3
    По моему опыту, Emacs не очень хорошо работает с очень большими файлами.
  • 0
    Можете ли вы выполнить это как действие по сценарию, или это только интерактивная опция?
1

Я хотел сделать то же самое из script с помощью переменной и достигнуть ее, поставив кавычки вокруг переменной $, чтобы отделить имя переменной от p:

sed -n "$first","$count"p imagelist.txt >"$imageblock"

Я хотел разбить список на отдельные папки и найти начальный вопрос и ответить на полезный шаг. (команда split не является опцией на старых os, я должен передать код).

1

Это может сработать для вас (GNU sed):

sed -ne '16224,16482w newfile' -e '16482q' file

или используя bash:

sed -n $'16224,16482w newfile\n16482q' file
1

Я написал небольшой bash script, который можно запустить из командной строки, пока вы обновляете свой PATH, чтобы включить его каталог (или вы можете поместить его в каталог, который уже содержится в PATH).

Использование: $pinch filename start-line end-line

#!/bin/bash
# Display line number ranges of a file to the terminal.
# Usage: $ pinch filename start-line end-line
# By Evan J. Coon

FILENAME=$1
START=$2
END=$3

ERROR="[PINCH ERROR]"

# Check that the number of arguments is 3
if [ $# -lt 3 ]; then
    echo "$ERROR Need three arguments: Filename Start-line End-line"
    exit 1
fi

# Check that the file exists.
if [ ! -f "$FILENAME" ]; then
    echo -e "$ERROR File does not exist. \n\t$FILENAME"
    exit 1
fi

# Check that start-line is not greater than end-line
if [ "$START" -gt "$END" ]; then
    echo -e "$ERROR Start line is greater than End line."
    exit 1
fi

# Check that start-line is positive.
if [ "$START" -lt 0 ]; then
    echo -e "$ERROR Start line is less than 0."
    exit 1
fi

# Check that end-line is positive.
if [ "$END" -lt 0 ]; then
    echo -e "$ERROR End line is less than 0."
    exit 1
fi

NUMOFLINES=$(wc -l < "$FILENAME")

# Check that end-line is not greater than the number of lines in the file.
if [ "$END" -gt "$NUMOFLINES" ]; then
    echo -e "$ERROR End line is greater than number of lines in file."
    exit 1
fi

# The distance from the end of the file to end-line
ENDDIFF=$(( NUMOFLINES - END ))

# For larger files, this will run more quickly. If the distance from the
# end of the file to the end-line is less than the distance from the
# start of the file to the start-line, then start pinching from the
# bottom as opposed to the top.
if [ "$START" -lt "$ENDDIFF" ]; then
    < "$FILENAME" head -n $END | tail -n +$START
else
    < "$FILENAME" tail -n +$START | head -n $(( END-START+1 ))
fi

# Success
exit 0
  • 1
    Это медленнее, чем многие альтернативы, потому что он использует 2 команды, где 1 достаточно. Фактически, он читает файл дважды из-за команды wc , которая тратит пропускную способность диска, особенно на гигабайтные файлы. Во всех отношениях это хорошо документировано, но это также излишнее инженерное решение.
0

Поскольку мы говорим об извлечении строк текста из текстового файла, я дам специальный случай, когда вы хотите извлечь все строки, соответствующие определенному шаблону.

myfile content:
=====================
line1 not needed
line2 also discarded
[Data]
first data line
second data line
=====================
sed -n '/Data/,$p' myfile

Распечатает строку [Данные] и остальные. Если вы хотите, чтобы текст от строки1 к шаблону, вы вводите: sed -n '1,/Data/p' myfile. Кроме того, если вы знаете два шаблона (лучше быть уникальными в тексте), как начальную, так и конечную строки диапазона можно указать со спичками.

sed -n '/BEGIN_MARK/,/END_MARK/p' myfile
0

В ответах-ответа принимается -n. Здесь другой способ, если вы склонны.

cat $filename | sed "${linenum}p;d";

Это делает следующее:

  • в содержимом файла (или введите текст, который вы хотите).
  • sed выбирает данную строку, печатает ее
  • d требуется для удаления строк, иначе sed будет считать, что все строки будут напечатаны. то есть без d, вы получите все строки, напечатанные выбранной строкой, напечатанными дважды, потому что у вас есть часть ${linenum} p, запрашивающая ее печать. Я почти уверен, что -n в основном делает то же самое, что и здесь.
  • 3
    примечание cat file | sed лучше записать как sed file
  • 0
    Кроме того, это просто печатает строку, тогда как вопрос о диапазоне их.
-4

Я думаю, что это может быть полезным решением. Если имя таблицы "человек", вы можете использовать sed для получения всех строк, необходимых для восстановления таблицы.

sed -n -e '/DROP TABLE IF EXISTS.*`person `/,/UNLOCK TABLES/p' data.sql  > new_data.sql

Основываясь на этом ответе, в котором отсутствует таблица "DROP TABLE IF EXIST" для таблицы, которую вы восстанавливаете, и вам нужно удалить несколько строк со дна перед тем как использовать его, чтобы предотвратить удаление следующей таблицы.

Подробную информацию также можно найти здесь

Ещё вопросы

Сообщество Overcoder
Наверх
Меню