Есть ли «канонический» способ сделать это? Я использовал, head -n | tail -1
что делает трюк, но мне было интересно, есть ли инструмент Bash, который специально извлекает строку (или диапазон строк) из файла.
Под «каноническим» я подразумеваю программу, основной функцией которой является это.
awk
и,sed
и я уверен, что кто-то может придумать Perl с однойhead | tail
решение является неоптимальным. Другие более почти оптимальные решения были предложены.head | tail
Решение не работает, если вы запрашиваете строку , которая не существует на входе: он будет печатать последнюю строку.Ответы:
head
и труба сtail
будет медленной для огромного файла. Я хотел бы предложить,sed
как это:Где
NUM
номер строки, которую вы хотите напечатать; так, например,sed '10q;d' file
для печати 10-й строкиfile
.Объяснение:
NUMq
будет немедленно выйти , когда номер строкиNUM
.d
удалит строку вместо ее печати; это запрещено в последней строке, потому чтоq
остальные сценарии пропускаются при выходе.Если у вас есть
NUM
переменная, вы захотите использовать двойные кавычки вместо одинарных:источник
sed -n 'NUMp'
иsed 'NUM!d'
решения , предложенные ниже.tail -n+NUM file | head -n1
, вероятно, будет так же быстро или быстрее. По крайней мере, это было (значительно) быстрее в моей системе, когда я попробовал его с NUM 250000 для файла с полмиллиона строк. YMMV, но я не понимаю почему.cat
действительно быстрее (почти в два раза быстрее), но только если файл еще не был кэширован . Как только файл кэшируется , прямое использование аргумента имени файла происходит быстрее (примерно на 1/3 быстрее), аcat
производительность остается неизменной. Любопытно, что в OS X 10.9.3, похоже, ничего из этого не имеет значения:cat
/ nocat
, файл кэширован или нет. @anubhava: мое удовольствие.sed 'NUMq
выведет первыеNUM
файлы и;d
удалит все, кроме последней строки.напечатает 2-ю строку
2011-я линия
строка 10 до строки 33
1-я и 3-я линия
и так далее...
Для добавления строк с помощью sed вы можете проверить это:
sed: вставить строку в определенную позицию
источник
<
в этом случае не является необходимым. Проще говоря, я предпочитаю использовать перенаправления, потому что я часто использовал перенаправления, напримерsed -n '100p' < <(some_command)
, универсальный синтаксис :). Это НЕ менее эффективно, потому что перенаправление выполняется с помощью shell при разветвлении самого себя, так что ... это всего лишь предпочтение ... (и да, это на один символ длиннее) :)head
/tail
не решаетsed -n '1p;3p'
сценарий - он же печатает больше несмежных строк ...У меня уникальная ситуация, когда я могу сравнить решения, предложенные на этой странице, и поэтому я пишу этот ответ как консолидацию предложенных решений с включенным временем выполнения для каждого.
Настроить
У меня есть файл текстовых данных ASCII 3,261 гигабайта с одной парой ключ-значение на строку. Файл содержит 3,339,550,320 строк в общей сложности и не открывается в любом редакторе, который я пробовал, включая мой переход к Vim. Мне нужно поместить этот файл в подмножество, чтобы исследовать некоторые из обнаруженных мной значений, начиная только с строки ~ 500 000 000.
Поскольку файл имеет так много строк:
Мой лучший вариант развития событий - это решение, которое извлекает только одну строку из файла, не считывая другие строки в файле, но я не могу думать о том, как бы это сделать в Bash.
В целях моего здравомыслия я не собираюсь читать полные 500 000 000 строк, которые мне нужны для моей собственной проблемы. Вместо этого я попытаюсь извлечь строку 50 000 000 из 3 339 550 320 (что означает, что чтение полного файла займет в 60 раз больше времени, чем необходимо).
Я буду использовать
time
встроенный для тестирования каждой команды.базисный
Сначала давайте посмотрим, как
head
tail
решение:Базовая линия для строки 50 миллионов составляет 00: 01: 15.321, если бы я сразу выбрал строку 500 миллионов, это, вероятно, составило бы ~ 12,5 минут.
резать
Я сомневаюсь в этом, но оно того стоит:
Для этого потребовалось 00: 05: 12.156, что намного медленнее, чем базовый уровень! Я не уверен, прочитал ли он весь файл или только до 50 миллионов строк перед остановкой, но, несмотря на это, это не кажется эффективным решением проблемы.
AWK
Я запустил решение только
exit
потому, что не собирался ждать запуска полного файла:Этот код работал в 00: 01: 16.583, что всего на ~ 1 секунду медленнее, но все же не улучшило базовый уровень. При такой скорости, если команда выхода была исключена, вероятно, потребовалось бы ~ 76 минут, чтобы прочитать весь файл!
Perl
Я также запустил существующее решение Perl:
Этот код работал в 00: 01: 13.146, что примерно на 2 секунды быстрее, чем базовый уровень. Если бы я запустил его на 500 000 000, это заняло бы ~ 12 минут.
СЭД
Главный ответ на доске, вот мой результат:
Этот код работал в 00: 01: 12.705, что на 3 секунды быстрее, чем базовая линия, и на ~ 0,4 секунды быстрее, чем Perl. Если бы я запустил все 500 000 000 строк, это заняло бы ~ 12 минут.
файле проекта
У меня есть bash 3.1, и поэтому я не могу проверить решение mapfile.
Вывод
Похоже, что по большей части трудно улучшить
head
tail
решение. В лучшем случаеsed
решение обеспечивает повышение эффективности на ~ 3%.(проценты рассчитываются по формуле
% = (runtime/baseline - 1) * 100
)Ряд 50 000 000
sed
perl
head|tail
awk
cut
Ряд 500 000 000
sed
perl
head|tail
awk
cut
Строка 3,338,559,320
sed
perl
head|tail
awk
cut
источник
С
awk
этим довольно быстроЕсли это верно, то по умолчанию
awk
выполняется:{print $0}
.Альтернативные версии
Если ваш файл окажется огромным, вам лучше
exit
почитать нужную строку. Таким образом, вы экономите время процессора. Смотрите сравнение времени в конце ответа .Если вы хотите дать номер строки из переменной bash, вы можете использовать:
Посмотрите, сколько времени сэкономлено с помощью
exit
, особенно если строка находится в первой части файла:Таким образом, разница составляет 0,198 с против 1,303 с, примерно в 6 раз быстрее.
источник
awk 'BEGIN{FS=RS}(NR == num_line) {print; exit}' file
awk 'FNR==n' n=10 file1 n=30 file2 n=60 file3
. С GNU awk это можно ускорить с помощьюawk 'FNR==n{print;nextfile}' n=10 file1 n=30 file2 n=60 file3
.FS=RS
избегает расщепления поля?FS=RS
не избежать разделение полей, но он разбирает только $ 0 из них и только не присваивает одно поле , потому что нетRS
в$0
FS=RS
и не видел разницы во времени. Как насчет того, чтобы я задал вопрос об этом, чтобы вы могли расширяться? Спасибо!Согласно моим тестам, с точки зрения производительности и читабельности моя рекомендация:
tail -n+N | head -1
N
это номер строки, которую вы хотите. Например,tail -n+7 input.txt | head -1
напечатает 7-ю строку файла.tail -n+N
напечатает все, начиная со строкиN
, иhead -1
остановит его после одной строки.Альтернатива
head -N | tail -1
, возможно, немного более читабельна. Например, это напечатает 7-ую строку:head -7 input.txt | tail -1
Когда дело доходит до производительности, для небольших размеров нет большой разницы, но она будет опережать
tail | head
(сверху), когда файлы станут большими.Топ проголосовавших
sed 'NUMq;d'
Интересно узнать, кто набрал больше , но я бы сказал, что его поймут меньше людей, чем решение «голова / хвост», и оно будет медленнее, чем «хвост / голова».В моих тестах обе версии хвоста / головы превзошли
sed 'NUMq;d'
последовательно . Это соответствует другим критериям, которые были опубликованы. Трудно найти случай, когда хвосты / головы были действительно плохими. Это также неудивительно, поскольку вы ожидаете, что эти операции будут сильно оптимизированы в современной системе Unix.Чтобы понять разницу в производительности, вот число, которое я получаю для огромного файла (9.3G):
tail -n+N | head -1
: 3,7 сhead -N | tail -1
: 4,6 секsed Nq;d
: 18,8 секРезультаты могут отличаться, но производительность
head | tail
иtail | head
, в общем, сопоставима для меньших входных данных иsed
всегда медленнее значительным фактором (примерно в 5 раз или около того).Чтобы воспроизвести мой тест, вы можете попробовать следующее, но имейте в виду, что он создаст файл 9.3G в текущем рабочем каталоге:
Вот результат запуска на моей машине (ThinkPad X1 Carbon с SSD и 16G памяти). Я предполагаю, что в конечном счете все будет происходить из кэша, а не с диска:
источник
head | tail
vstail | head
? Или это зависит от того, какая строка печатается (начало файла или конец файла)?head -5 | tail -1
противtail -n+5 | head -1
. На самом деле, я нашел другой ответ, который сделал тестовое сравнение и оказалсяtail | head
быстрее. stackoverflow.com/a/48189289Вау, все возможности!
Попробуй это:
или один из них в зависимости от вашей версии Awk:
( Вы , возможно , придется попробовать
nawk
илиgawk
команду ).Есть ли инструмент, который печатает только эту строку? Не один из стандартных инструментов. Тем не менее,
sed
вероятно, самый близкий и простой в использовании.источник
Полезные однострочные скрипты для sed
источник
Этот вопрос помечен как Bash, вот способ Bash (≥4): используйте
mapfile
с опциями-s
(пропустить) и-n
(количество).Если вам нужно получить 42-ю строку файла
file
:На этом этапе у вас будет массив,
ary
поля которого содержат строкиfile
(включая завершающий символ новой строки), где мы пропустили первые 41 строку (-s 41
) и остановились после чтения одной строки (-n 1
). Так что это действительно 42-я линия. Чтобы распечатать это:Если вам нужен диапазон строк, скажите диапазон 42–666 (включительно) и скажите, что не хотите выполнять математику самостоятельно, и напечатайте их на стандартный вывод:
Если вам нужно обработать и эти строки, не очень удобно хранить завершающий перевод новой строки. В этом случае используйте
-t
параметр (отделка):Вы можете сделать функцию, которая сделает это за вас:
Никаких внешних команд, только встроенные команды Bash!
источник
Вы также можете использовать sed print и выйти:
источник
-n
Опция отключает действие по умолчанию для печати каждой строки, так как , конечно , вы бы обнаружили на быстрый взгляд на странице человека.sed
всеsed
ответы примерно одинаковы. Поэтому (для GNUsed
) это лучшийsed
ответ, поскольку это сэкономит время для больших файлов и небольших значений n-й строки .Вы также можете использовать Perl для этого:
источник
Самым быстрым решением для больших файлов всегда является tail | head при условии, что два расстояния:
S
E
известны. Тогда мы могли бы использовать это:
howmany это просто количество необходимых строк.
Еще несколько подробностей в https://unix.stackexchange.com/a/216614/79743
источник
S
иE
(т. Е. Байты, символы или строки).Все вышеперечисленные ответы прямо отвечают на вопрос. Но вот менее прямое решение, но потенциально более важная идея, чтобы вызвать мысль.
Поскольку длины строк произвольны, все байты файла до n-й строки должны прочитать . Если у вас большой файл или вам нужно многократно повторять эту задачу, и этот процесс отнимает много времени, тогда вам следует серьезно подумать о том, следует ли вам в первую очередь хранить свои данные другим способом.
Реальное решение состоит в том, чтобы иметь индекс, например, в начале файла, указывающий позиции, где начинаются строки. Вы можете использовать формат базы данных или просто добавить таблицу в начале файла. Или создайте отдельный индексный файл, который будет сопровождать ваш большой текстовый файл.
Например, вы можете создать список позиций символов для новых строк:
затем прочитайте
tail
, что на самом деле находитсяseek
непосредственно в соответствующей точке файла!например, чтобы получить линию 1000:
источник
В качестве продолжения очень полезного ответа по тестированию CaffeineConnoisseur ... Мне было любопытно, насколько быстро метод «mapfile» сравнивается с другими (так как он не тестировался), поэтому я попробовал быстрое и грязное сравнение скорости, как У меня есть Bash 4 удобно. Вбросил тест метода «хвост | голова» (а не «голова | хвост»), упомянутого в одном из комментариев к верхнему ответу, пока я был на нем, поскольку люди поют его похвалы. У меня нет ничего похожего на размер тестового файла; лучшее, что я смог найти за короткий срок, это файл родословной 14M (длинные строки, разделенные пробелами, чуть меньше 12000 строк).
Короткая версия: mapfile появляется быстрее, чем метод cut, но медленнее, чем все остальное, поэтому я бы назвал это неудачным. хвост | head, OTOH, похоже, что он может быть самым быстрым, хотя с файлом такого размера разница не столь существенна по сравнению с sed.
Надеюсь это поможет!
источник
Используя то, что упоминали другие, я хотел, чтобы это было быстрой и удобной функцией в моей оболочке bash.
Создать файл:
~/.functions
Добавьте к этому содержание:
getline() { line=$1 sed $line'q;d' $2 }
Затем добавьте это в свой
~/.bash_profile
:source ~/.functions
Теперь, когда вы открываете новое окно bash, вы можете просто вызвать функцию следующим образом:
getline 441 myfile.txt
источник
Если вы получили несколько строк, разделенных \ n (обычно новая строка). Вы также можете использовать «вырезать»:
Вы получите вторую строку из файла.
-f3
дает вам 3-ю строку.источник
cat FILE | cut -f2,5 -d$'\n'
отобразятся строки 2 и 5 ФАЙЛА. (Но это не сохранит порядок.)Чтобы напечатать n-ю строку, используя sed с переменной в качестве номера строки:
Здесь флаг '-e' предназначен для добавления скрипта в команду для выполнения.
источник
Уже много хороших ответов. Я лично иду с 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
источник
После того, как взглянуть на верхний ответ и в тесте , я реализовал крошечную вспомогательную функцию:
В основном вы можете использовать его двумя способами:
источник
Я поместил некоторые из приведенных выше ответов в короткий скрипт bash, который вы можете поместить в файл с именем
get.sh
и ссылкой/usr/local/bin/get
(или любым другим именем, которое вы предпочитаете).Убедитесь, что он исполняется с
Свяжите это, чтобы сделать это доступным
PATH
сНаслаждайтесь ответственно!
п
источник