Инструмент Bash для получения n-й строки из файла

606

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

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

Влад Вивдович
источник
10
«Путь Unix» состоит в том, чтобы объединить инструменты, которые хорошо выполняют свою работу. Поэтому я думаю, что вы уже нашли очень подходящий метод. Другие методы включают awkи, sedи я уверен, что кто-то может придумать Perl с одной
строкой
3
Двойная команда предполагает, что head | tailрешение является неоптимальным. Другие более почти оптимальные решения были предложены.
Джонатан Леффлер
Проводили ли вы какие-либо тесты, на которых решение является самым быстрым для среднего случая?
Марчин
5
Тесты (для диапазона) в строке cat X и строке Y огромного файла в Unix & Linux . (cc @Marcin, если вам все еще интересно после двух с лишним лет)
Кевин
6
head | tailРешение не работает, если вы запрашиваете строку , которая не существует на входе: он будет печатать последнюю строку.
Ярно

Ответы:

803

headи труба с tailбудет медленной для огромного файла. Я хотел бы предложить, sedкак это:

sed 'NUMq;d' file

Где NUMномер строки, которую вы хотите напечатать; так, например, sed '10q;d' fileдля печати 10-й строкиfile .

Объяснение:

NUMqбудет немедленно выйти , когда номер строки NUM.

dудалит строку вместо ее печати; это запрещено в последней строке, потому что qостальные сценарии пропускаются при выходе.

Если у вас есть NUMпеременная, вы захотите использовать двойные кавычки вместо одинарных:

sed "${NUM}q;d" file
анубхава
источник
44
Для тех , кому интересно, это решение кажется около 6 до 9 раз быстрее , чем sed -n 'NUMp'и sed 'NUM!d'решения , предложенные ниже.
Скиппи ле Гран Гуру
75
Я думаю tail -n+NUM file | head -n1, вероятно, будет так же быстро или быстрее. По крайней мере, это было (значительно) быстрее в моей системе, когда я попробовал его с NUM 250000 для файла с полмиллиона строк. YMMV, но я не понимаю почему.
Ричи
2
@rici (редакция предыдущего комментария) В Linux (Ubuntu 12.04, Fedora 20) использование catдействительно быстрее (почти в два раза быстрее), но только если файл еще не был кэширован . Как только файл кэшируется , прямое использование аргумента имени файла происходит быстрее (примерно на 1/3 быстрее), а catпроизводительность остается неизменной. Любопытно, что в OS X 10.9.3, похоже, ничего из этого не имеет значения: cat/ no cat, файл кэширован или нет. @anubhava: мое удовольствие.
mklement0
2
@SkippyleGrandGourou: Учитывая специфику этой оптимизации , даже ваши диапазоны чисел бессмысленны как общее утверждение . Единственный общий вывод заключается в следующем: (а) эта оптимизация может быть безопасно применена ко всем входным данным, (б) эффекты будут варьироваться от нуля до драматических , в зависимости от индекса искомой линии по отношению к количеству общих линий.
mklement0
17
sed 'NUMqвыведет первые NUMфайлы и ;dудалит все, кроме последней строки.
анубхава
304
sed -n '2p' < file.txt

напечатает 2-ю строку

sed -n '2011p' < file.txt

2011-я линия

sed -n '10,33p' < file.txt

строка 10 до строки 33

sed -n '1p;3p' < file.txt

1-я и 3-я линия

и так далее...

Для добавления строк с помощью sed вы можете проверить это:

sed: вставить строку в определенную позицию

jm666
источник
6
@RafaelBarbosa <в этом случае не является необходимым. Проще говоря, я предпочитаю использовать перенаправления, потому что я часто использовал перенаправления, например sed -n '100p' < <(some_command), универсальный синтаксис :). Это НЕ менее эффективно, потому что перенаправление выполняется с помощью shell при разветвлении самого себя, так что ... это всего лишь предпочтение ... (и да, это на один символ длиннее) :)
jm666
1
@ jm666 На самом деле это на 2 символа длиннее, так как вы обычно ставите «<» и дополнительный пробел »после« вместо одного пробела, если вы не использовали <:)
rasen58
2
@ rasen58 пробел тоже символ? :) / ладно, шучу - ты прав / :)
jm666
1
@duhaime конечно, если кто-то должен сделать оптимизации. Но ИМХО для «общих» проблем это нормально и разница незаметна. Кроме того, сценарий head/ tailне решает sed -n '1p;3p'сценарий - он же печатает больше несмежных строк ...
jm666
1
@duhaime конечно - заметка правильная и нужна. :)
jm666
93

У меня уникальная ситуация, когда я могу сравнить решения, предложенные на этой странице, и поэтому я пишу этот ответ как консолидацию предложенных решений с включенным временем выполнения для каждого.

Настроить

У меня есть файл текстовых данных ASCII 3,261 гигабайта с одной парой ключ-значение на строку. Файл содержит 3,339,550,320 строк в общей сложности и не открывается в любом редакторе, который я пробовал, включая мой переход к Vim. Мне нужно поместить этот файл в подмножество, чтобы исследовать некоторые из обнаруженных мной значений, начиная только с строки ~ 500 000 000.

Поскольку файл имеет так много строк:

  • Мне нужно извлечь только подмножество строк, чтобы сделать что-нибудь полезное с данными.
  • Чтение каждой строки, приводящей к ценностям, которые меня интересуют, займет много времени.
  • Если решение прочитает строки, которые мне интересны, и продолжит чтение остальной части файла, оно потратит время на чтение почти 3 миллиардов ненужных строк и займет в 6 раз больше времени, чем необходимо.

Мой лучший вариант развития событий - это решение, которое извлекает только одну строку из файла, не считывая другие строки в файле, но я не могу думать о том, как бы это сделать в Bash.

В целях моего здравомыслия я не собираюсь читать полные 500 000 000 строк, которые мне нужны для моей собственной проблемы. Вместо этого я попытаюсь извлечь строку 50 000 000 из 3 339 550 320 (что означает, что чтение полного файла займет в 60 раз больше времени, чем необходимо).

Я буду использовать timeвстроенный для тестирования каждой команды.

базисный

Сначала давайте посмотрим, как head tailрешение:

$ time head -50000000 myfile.ascii | tail -1
pgm_icnt = 0

real    1m15.321s

Базовая линия для строки 50 миллионов составляет 00: 01: 15.321, если бы я сразу выбрал строку 500 миллионов, это, вероятно, составило бы ~ 12,5 минут.

резать

Я сомневаюсь в этом, но оно того стоит:

$ time cut -f50000000 -d$'\n' myfile.ascii
pgm_icnt = 0

real    5m12.156s

Для этого потребовалось 00: 05: 12.156, что намного медленнее, чем базовый уровень! Я не уверен, прочитал ли он весь файл или только до 50 миллионов строк перед остановкой, но, несмотря на это, это не кажется эффективным решением проблемы.

AWK

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

$ time awk 'NR == 50000000 {print; exit}' myfile.ascii
pgm_icnt = 0

real    1m16.583s

Этот код работал в 00: 01: 16.583, что всего на ~ 1 секунду медленнее, но все же не улучшило базовый уровень. При такой скорости, если команда выхода была исключена, вероятно, потребовалось бы ~ 76 минут, чтобы прочитать весь файл!

Perl

Я также запустил существующее решение Perl:

$ time perl -wnl -e '$.== 50000000 && print && exit;' myfile.ascii
pgm_icnt = 0

real    1m13.146s

Этот код работал в 00: 01: 13.146, что примерно на 2 секунды быстрее, чем базовый уровень. Если бы я запустил его на 500 000 000, это заняло бы ~ 12 минут.

СЭД

Главный ответ на доске, вот мой результат:

$ time sed "50000000q;d" myfile.ascii
pgm_icnt = 0

real    1m12.705s

Этот код работал в 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

  1. 00: 01: 12,705 (-00: 00: 02,616 = -3,47%) sed
  2. 00: 01: 13.146 (-00: 00: 02.175 = -2,89%) perl
  3. 00: 01: 15,321 (+00: 00: 00.000 = + 0,00%) head|tail
  4. 00: 01: 16,583 (+00: 00: 01.262 = + 1,68%) awk
  5. 00: 05: 12,156 (+00: 03: 56,835 = + 314,43%) cut

Ряд 500 000 000

  1. 00: 12: 07.050 (-00: 00: 26.160) sed
  2. 00: 12: 11.460 (-00: 00: 21.750) perl
  3. 00: 12: 33.210 (+00: 00: 00.000) head|tail
  4. 00: 12: 45.830 (+00: 00: 12.620) awk
  5. 00: 52: 01.560 (+00: 40: 31.650) cut

Строка 3,338,559,320

  1. 01: 20: 54.599 (-00: 03: 05.327) sed
  2. 01: 21: 24.045 (-00: 02: 25.227) perl
  3. 01: 23: 49.273 (+00: 00: 00.000) head|tail
  4. 01: 25: 13.548 (+00: 02: 35.735) awk
  5. 05: 47: 23.026 (+04: 24: 26.246) cut
CaffeineConnoisseur
источник
4
Интересно, сколько времени займет просто закачивание всего файла в / dev / null. (Что, если бы это был только тест жесткого диска?)
sanmai
Я чувствую извращенное желание преклониться перед твоим владением словарем текстовых файлов 3+. Безотносительно обоснования, это так охватывает текстуальность :)
Stabledog
51

С awkэтим довольно быстро

awk 'NR == num_line' file

Если это верно, то по умолчанию awkвыполняется: {print $0}.


Альтернативные версии

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

awk 'NR == num_line {print; exit}' file

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

awk 'NR == n' n=$num file
awk -v n=$num 'NR == n' file   # equivalent

Посмотрите, сколько времени сэкономлено с помощью exit, особенно если строка находится в первой части файла:

# Let's create a 10M lines file
for ((i=0; i<100000; i++)); do echo "bla bla"; done > 100Klines
for ((i=0; i<100; i++)); do cat 100Klines; done > 10Mlines

$ time awk 'NR == 1234567 {print}' 10Mlines
bla bla

real    0m1.303s
user    0m1.246s
sys 0m0.042s
$ time awk 'NR == 1234567 {print; exit}' 10Mlines
bla bla

real    0m0.198s
user    0m0.178s
sys 0m0.013s

Таким образом, разница составляет 0,198 с против 1,303 с, примерно в 6 раз быстрее.

Федорки "ТАК прекратить вредить"
источник
Этот метод всегда будет медленнее, потому что awk пытается выполнить разбиение поля. Накладные расходы на разделение полей могут быть уменьшены наawk 'BEGIN{FS=RS}(NR == num_line) {print; exit}' file
kvantour
Реальная сила AWK в этом методе идет вперед , когда вы хотите , чтобы сцепить строки n1 из file1, file2 п2, п3 или file3 ... 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.
Квантур
@kvantour действительно, следующий файл GNU awk отлично подходит для таких вещей. Как получилось, FS=RSизбегает расщепления поля?
Федорки "ТАК прекрати вредить"
1
FS=RSне избежать разделение полей, но он разбирает только $ 0 из них и только не присваивает одно поле , потому что нет RSв$0
kvantour
@kvantour Я проводил некоторые тесты FS=RSи не видел разницы во времени. Как насчет того, чтобы я задал вопрос об этом, чтобы вы могли расширяться? Спасибо!
fedorqui 'ТАК ХОТИТЕ вредить'
29

Согласно моим тестам, с точки зрения производительности и читабельности моя рекомендация:

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 в текущем рабочем каталоге:

#!/bin/bash
readonly file=tmp-input.txt
readonly size=1000000000
readonly pos=500000000
readonly retries=3

seq 1 $size > $file
echo "*** head -N | tail -1 ***"
for i in $(seq 1 $retries) ; do
    time head "-$pos" $file | tail -1
done
echo "-------------------------"
echo
echo "*** tail -n+N | head -1 ***"
echo

seq 1 $size > $file
ls -alhg $file
for i in $(seq 1 $retries) ; do
    time tail -n+$pos $file | head -1
done
echo "-------------------------"
echo
echo "*** sed Nq;d ***"
echo

seq 1 $size > $file
ls -alhg $file
for i in $(seq 1 $retries) ; do
    time sed $pos'q;d' $file
done
/bin/rm $file

Вот результат запуска на моей машине (ThinkPad X1 Carbon с SSD и 16G памяти). Я предполагаю, что в конечном счете все будет происходить из кэша, а не с диска:

*** head -N | tail -1 ***
500000000

real    0m9,800s
user    0m7,328s
sys     0m4,081s
500000000

real    0m4,231s
user    0m5,415s
sys     0m2,789s
500000000

real    0m4,636s
user    0m5,935s
sys     0m2,684s
-------------------------

*** tail -n+N | head -1 ***

-rw-r--r-- 1 phil 9,3G Jan 19 19:49 tmp-input.txt
500000000

real    0m6,452s
user    0m3,367s
sys     0m1,498s
500000000

real    0m3,890s
user    0m2,921s
sys     0m0,952s
500000000

real    0m3,763s
user    0m3,004s
sys     0m0,760s
-------------------------

*** sed Nq;d ***

-rw-r--r-- 1 phil 9,3G Jan 19 19:50 tmp-input.txt
500000000

real    0m23,675s
user    0m21,557s
sys     0m1,523s
500000000

real    0m20,328s
user    0m18,971s
sys     0m1,308s
500000000

real    0m19,835s
user    0m18,830s
sys     0m1,004s
Филипп Классен
источник
1
Отличается ли производительность между head | tailvs tail | head? Или это зависит от того, какая строка печатается (начало файла или конец файла)?
18:18
1
@wisbucky У меня нет точных цифр, но один недостаток, заключающийся в том, чтобы сначала использовать хвост, а затем "голову -1", заключается в том, что вам нужно заранее знать общую длину. Если вы этого не знаете, вам придется сначала посчитать это, что приведет к потере производительности. Другим недостатком является то, что он менее интуитивно понятен в использовании. Например, если у вас число от 1 до 10, и вы хотите получить 3-ю строку, вам придется использовать «tail -8 | head -1». Это более подвержено ошибкам, чем "head -3 | tail -1".
Филипп Классен
извините, я должен был включить пример, чтобы быть ясным. head -5 | tail -1против tail -n+5 | head -1. На самом деле, я нашел другой ответ, который сделал тестовое сравнение и оказался tail | headбыстрее. stackoverflow.com/a/48189289
wisbucky
1
@wisbucky Спасибо, что упомянули об этом! Я сделал несколько тестов и должен согласиться, что это всегда было немного быстрее, независимо от положения линии от того, что я видел. Учитывая это, я изменил свой ответ, а также включил тест на случай, если кто-то захочет его воспроизвести.
Филипп Классен
27

Вау, все возможности!

Попробуй это:

sed -n "${lineNum}p" $file

или один из них в зависимости от вашей версии Awk:

awk  -vlineNum=$lineNum 'NR == lineNum {print $0}' $file
awk -v lineNum=4 '{if (NR == lineNum) {print $0}}' $file
awk '{if (NR == lineNum) {print $0}}' lineNum=$lineNum $file

( Вы , возможно , придется попробовать nawkили gawkкоманду ).

Есть ли инструмент, который печатает только эту строку? Не один из стандартных инструментов. Тем не менее, sedвероятно, самый близкий и простой в использовании.

Дэвид В.
источник
21

Этот вопрос помечен как Bash, вот способ Bash (≥4): используйте mapfileс опциями -s(пропустить) и -n(количество).

Если вам нужно получить 42-ю строку файла file:

mapfile -s 41 -n 1 ary < file

На этом этапе у вас будет массив, aryполя которого содержат строки file(включая завершающий символ новой строки), где мы пропустили первые 41 строку ( -s 41) и остановились после чтения одной строки ( -n 1). Так что это действительно 42-я линия. Чтобы распечатать это:

printf '%s' "${ary[0]}"

Если вам нужен диапазон строк, скажите диапазон 42–666 (включительно) и скажите, что не хотите выполнять математику самостоятельно, и напечатайте их на стандартный вывод:

mapfile -s $((42-1)) -n $((666-42+1)) ary < file
printf '%s' "${ary[@]}"

Если вам нужно обработать и эти строки, не очень удобно хранить завершающий перевод новой строки. В этом случае используйте -tпараметр (отделка):

mapfile -t -s $((42-1)) -n $((666-42+1)) ary < file
# do stuff
printf '%s\n' "${ary[@]}"

Вы можете сделать функцию, которая сделает это за вас:

print_file_range() {
    # $1-$2 is the range of file $3 to be printed to stdout
    local ary
    mapfile -s $(($1-1)) -n $(($2-$1+1)) ary < "$3"
    printf '%s' "${ary[@]}"
}

Никаких внешних команд, только встроенные команды Bash!

gniourf_gniourf
источник
11

Вы также можете использовать sed print и выйти:

sed -n '10{p;q;}' file   # print line 10
Бернд
источник
6
-nОпция отключает действие по умолчанию для печати каждой строки, так как , конечно , вы бы обнаружили на быстрый взгляд на странице человека.
tripleee
В GNU sed все sedответы примерно одинаковы. Поэтому (для GNU sed ) это лучший sedответ, поскольку это сэкономит время для больших файлов и небольших значений n-й строки .
АРУ
7

Вы также можете использовать Perl для этого:

perl -wnl -e '$.== NUM && print && exit;' some.file
Тимофей Столбов
источник
6

Самым быстрым решением для больших файлов всегда является 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

Сообщество
источник
1
Пожалуйста, уточните единицы измерения Sи E(т. Е. Байты, символы или строки).
agc
6

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

Поскольку длины строк произвольны, все байты файла до 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 - нет.
  • Я не проверял это в отношении большого файла.
  • Также смотрите этот ответ .
  • В качестве альтернативы - разбить файл на более мелкие файлы!
Санджай Манохар
источник
5

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

Короткая версия: mapfile появляется быстрее, чем метод 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

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

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

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

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

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

source ~/.functions

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

getline 441 myfile.txt

Марк Шуст в М.академии
источник
3

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

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

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

danger89
источник
1
Может также использоваться для отображения нескольких строк: cat FILE | cut -f2,5 -d$'\n'отобразятся строки 2 и 5 ФАЙЛА. (Но это не сохранит порядок.)
Андрей Макуха
2

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

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

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

aliasav
источник
2
Двоеточие является синтаксической ошибкой и должно быть точкой с запятой.
tripleee
2

Уже много хороших ответов. Я лично иду с 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

JJC
источник
1

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

function nth {
    if (( ${#} < 1 || ${#} > 2 )); then
        echo -e "usage: $0 \e[4mline\e[0m [\e[4mfile\e[0m]"
        return 1
    fi
    if (( ${#} > 1 )); then
        sed "$1q;d" $2
    else
        sed "$1q;d"
    fi
}

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

nth 42 myfile.txt
do_stuff | nth 42
Улисс Б.Н.
источник
0

Я поместил некоторые из приведенных выше ответов в короткий скрипт 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

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

п

поляризовать
источник