Инструмент Bash для получения n-й строки из файла - PullRequest
499 голосов
/ 16 мая 2011

Есть ли "канонический" способ сделать это?Я использовал head -n | tail -1, который добился цели, но мне было интересно, есть ли инструмент Bash, который специально извлекает строку (или диапазон строк) из файла.

"каноническим"Я имею в виду программу, главная функция которой это делает.

Ответы [ 18 ]

6 голосов
/ 17 июля 2015

Самым быстрым решением для больших файлов всегда является tail | head, при условии, что два расстояния:

  • от начала файла до начальной строки. Давайте назовем это S
  • расстояние от последней строки до конца файла. Будь это E

известны. Тогда мы могли бы использовать это:

mycount="$E"; (( E > S )) && mycount="+$S"
howmany="$(( endline - startline + 1 ))"
tail -n "$mycount"| head -n "$howmany"

howmany - это просто количество необходимых строк.

Еще немного подробностей в https://unix.stackexchange.com/a/216614/79743

4 голосов
/ 10 января 2018

В качестве ответа на очень полезный ответ CaffeineConnoisseur по сравнительному анализу ... Мне было любопытно, как быстро метод «mapfile» сравнивается с другими (так как он не тестировался), поэтому я попробовал быструю и грязную скоростьСравнение себя, как у меня есть Bash 4 удобно.Вбросил тест метода «хвост | голова» (а не «голова | хвост»), упомянутого в одном из комментариев к верхнему ответу, пока я был на нем, поскольку люди поют его похвалы.У меня нет ничего похожего на размер тестового файла;лучшее, что я смог найти за короткий срок, это файл родословной 14M (длинные строки, разделенные пробелами, чуть меньше 12000 строк).

Короткая версия: файл-файл отображается быстрее, чем метод cut, но медленнее, чем все остальноетак что я бы назвал это глупостью.хвост |head, OTOH, похоже, что он может быть самым быстрым, хотя с файлом такого размера разница не столь существенна по сравнению с sed.

$ time head -11000 [filename] | tail -1
[output redacted]

real    0m0.117s

$ time cut -f11000 -d$'\n' [filename]
[output redacted]

real    0m1.081s

$ time awk 'NR == 11000 {print; exit}' [filename]
[output redacted]

real    0m0.058s

$ time perl -wnl -e '$.== 11000 && print && exit;' [filename]
[output redacted]

real    0m0.085s

$ time sed "11000q;d" [filename]
[output redacted]

real    0m0.031s

$ time (mapfile -s 11000 -n 1 ary < [filename]; echo ${ary[0]})
[output redacted]

real    0m0.309s

$ time tail -n+11000 [filename] | head -n1
[output redacted]

real    0m0.028s

Надеюсь, это поможет!

4 голосов
/ 12 октября 2017

Все вышеперечисленные ответы прямо отвечают на вопрос.Но вот менее прямое решение, но потенциально более важная идея, чтобы вызвать мысль.

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

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

например, вы можете создать список позиций символов для новых строк:

awk 'BEGIN{c=0;print(c)}{c+=length()+1;print(c+1)}' file.txt > file.idx

, затем прочитать с помощью tail, что на самом деле seek непосредственно в соответствующую точку в файле!

например, чтобы получить строку 1000:

tail -c +$(awk 'NR=1000' file.idx) file.txt | head -1
  • Это может не работать с 2-байтовыми / многобайтовыми символами, так как awk "распознает символы", но tail не имеет.*
  • Я не проверял это на большом файле.
  • Также см. этот ответ .
  • В качестве альтернативы - разбейте файл на более мелкие файлы!
3 голосов
/ 07 января 2016

Если вы получили несколько строк, разделенных \ n (обычно новая строка). Вы также можете использовать «вырезать»:

echo "$data" | cut -f2 -d$'\n'

Вы получите вторую строку из файла. -f3 дает 3-ю строку.

2 голосов
/ 17 ноября 2017

Много хороших ответов уже. Я лично иду с awk. Для удобства, если вы используете bash, просто добавьте ниже к вашему ~/.bash_profile. И в следующий раз, когда вы войдете в систему (или если вы получите исходный файл .bash_profile после этого обновления), у вас будет новая отличная функция «nth», доступная для передачи ваших файлов.

Выполните это или поместите в свой ~ / .bash_profile (если используете bash) и снова откройте bash (или выполните source ~/.bach_profile)

# print just the nth piped in line nth () { awk -vlnum=${1} 'NR==lnum {print; exit}'; }

Тогда, чтобы использовать это, просто пройдите через него. Например,:.

$ yes line | cat -n | nth 5 5 line

1 голос
/ 17 января 2018

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

Создать файл: ~/.functions

Добавьте к нему содержимое:

getline() { line=$1 sed $line'q;d' $2 }

Затем добавьте это к своему ~/.bash_profile:

source ~/.functions

Теперь, когда вы открываете новое окно bash, вы можете просто вызвать функцию следующим образом:

getline 441 myfile.txt

1 голос
/ 13 марта 2015

Чтобы напечатать n-ю строку, используя sed с переменной в качестве номера строки:

a=4
sed -e $a'q:d' file

Здесь флаг '-e' предназначен для добавления скрипта в команду, которая будет выполнена.

0 голосов
/ 28 января 2019

Я поместил некоторые из приведенных выше ответов в короткий скрипт bash, который вы можете поместить в файл с именем get.sh и связать его с /usr/local/bin/get (или любым другим именем, которое вы предпочитаете).

#!/bin/bash
if [ "${1}" == "" ]; then
    echo "error: blank line number";
    exit 1
fi
re='^[0-9]+$'
if ! [[ $1 =~ $re ]] ; then
    echo "error: line number arg not a number";
    exit 1
fi
if [ "${2}" == "" ]; then
    echo "error: blank file name";
    exit 1
fi
sed "${1}q;d" $2;
exit 0

Убедитесь, что он исполняется с

$ chmod +x get

Свяжите его, чтобы сделать его доступным на PATH с

$ ln -s get.sh /usr/local/bin/get

Наслаждайтесь ответственно!

P

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...