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


604

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

Під "канонічною" я маю на увазі програму, основною функцією якої є це.


10
"Unix шлях" - це ланцюжок інструментів, які добре виконують свою роботу. Тож я думаю, ви вже знайшли дуже підходящий метод. Інші методи включають в себе, awkі sedя впевнений, що хтось може також придумати одноколірний Perl;)
0xC0000022L

3
Подвійна команда говорить про те, що head | tailрішення є неоптимальним. Запропоновано інші, майже оптимальні рішення.
Джонатан Леффлер

Чи проводили ви якісь орієнтири, рішення яких є найшвидшим для середнього випадку?
Марцін

5
Орієнтовні показники (для діапазону) у котячій лінії X до рядка Y у величезному файлі на Unix & Linux . (cc @Marcin, якщо ви все ще задумуєтесь через два роки)
Кевін

6
head | tailРішення не працює, якщо ви запитуєте рядок , яка не існує на вході: він буде друкувати останній рядок.
jarno

Відповіді:


800

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'запропоновані нижче.
Skippy le Grand Gourou

75
Я думаю tail -n+NUM file | head -n1, ймовірно, буде так само швидко чи швидше. Принаймні, це було (значно) швидше в моїй системі, коли я спробував це, коли NUM становив 250000 у файлі з півмільйона рядків. YMMV, але я не розумію, чому це було б.
rici

2
@rici (редакція попереднього коментаря) У Linux (Ubuntu 12.04, Fedora 20) використання catдійсно швидше (майже вдвічі швидше), але тільки якщо файл ще не був кешований . Після кешування файлу безпосередньо використання аргументу імені файлу відбувається швидше (приблизно на 1/3 швидше), а catпродуктивність залишається такою ж. Цікаво, що в OS X 10.9.3 ніщо з цього не має жодного значення: cat/ ні cat, кешований файл чи ні. @anubhava: моє задоволення.
mklement0

2
@SkippyleGrandGourou: Враховуючи специфіку цієї оптимізації , навіть ваші діапазони чисел є безглуздими як загальне твердження . Єдиним загальним результатом є таке: (a) цю оптимізацію можна безпечно застосувати до всіх вхідних даних, (b) ефекти будуть варіюватися від жодних до драматичних , залежно від індексу шуканого рядка відносно кількості загальних рядків.
mklement0

17
sed 'NUMqвиведе перші NUMфайли та ;dвидалить усі, крім останнього рядка.
anubhava

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: вставити рядок у певне положення


6
@RafaelBarbosa <в цьому випадку не потрібно. Просто, я вважаю за краще використовувати переадресації, тому що я часто використовував перенаправлення на зразок sed -n '100p' < <(some_command)- так, універсальний синтаксис :). Він НЕ менш ефективний, тому що перенаправлення робиться з оболонкою при самому розщепленні, так що ... це лише вподобання ... (і так, це на один символ більше) :)
jm666

1
@ jm666 Насправді це на 2 символи довше, оскільки ви зазвичай ставите "<", а також додатковий пробіл "" після <як проти одного простору, якщо ви не використовували <:)
rasen58

2
@ rasen58 простір теж є символом? :) / добре, просто жартую - ти
маєш

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 хвилин.

sed

Найвища відповідь на дошці, ось мій результат:

$ 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, тому не можу перевірити рішення картографічного файлу.

Висновок

Здається, здебільшого важко покращити 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

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уникнути розбиття поля?
fedorqui 'ТАК перестаньте шкодити'

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? Або це залежить від того, який рядок друкується (початок файлу проти кінця файлу)?
wisbucky

1
@wisbucky У мене немає важких цифр, але один недолік спочатку використовувати хвіст, а потім «голова -1» - це те, що потрібно знати загальну довжину заздалегідь. Якщо ви цього не знаєте, вам слід спершу порахувати це, що буде сприятливим для збитків. Ще один недолік полягає в тому, що його використовувати менш інтуїтивно. Наприклад, якщо у вас є число від 1 до 10 і ви хочете отримати 3-й рядок, вам доведеться використовувати "хвіст -8 | голова -1". Це більше схильність до помилок, ніж "голова -3 | хвіст -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(count) варіант.

Якщо вам потрібно отримати 42-й рядок файлу file:

mapfile -s 41 -n 1 ary < file

На цьому етапі у вас з'явиться масив, aryполя якого містять рядки file(включаючи кінцевий новий рядок), де ми пропустили перші 41 рядок ( -s 41) і зупинилися після читання одного рядка ( -n 1). Отже, це справді 42-й рядок. Щоб роздрукувати його:

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

Якщо вам потрібен діапазон ліній, скажіть діапазон 42–666 (включно), і скажіть, що ви не хочете займатися математикою самостійно, і надрукуйте їх на stdout:

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 вбудовані!


11

Ви також можете використовувати друк sed і вийти:

sed -n '10{p;q;}' file   # print line 10

6
Цей -nпараметр вимикає дію за замовчуванням для друку кожного рядка, як це, безумовно, ви могли б дізнатись швидким поглядом на підручну сторінку.
tripleee

У GNU sed всі sedвідповіді приблизно з однаковою швидкістю. Тому (для GNU sed ) це найкраща sedвідповідь, оскільки це заощадить час для великих файлів та малих значень n-го рядка .
agc


6

Найшвидше рішення для великих файлів - це завжди хвіст, за умови, що дві відстані:

  • від початку файлу до початкового рядка. Давайте назвемо це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
  • Це може не працювати з двобайтовими / багатобайтовими символами, оскільки awk "знає символів", але хвіст - ні.
  • Я не перевіряв це на великому файлі.
  • Дивіться також цю відповідь .
  • Як варіант - розділіть свій файл на менші файли!

5

Як слідку за дуже корисною відповіддю на тестування CaffeineConnoisseur ... Мені було цікаво, наскільки швидкий метод "mapfile" порівнювали з іншими (як це не перевіряли), тому я спробував швидко і брудно порівняти швидкість як У мене є баш 4 під рукою. Кинув тест на метод "хвіст | голова" (а не голова | хвіст), згаданий в одному з коментарів до головної відповіді, коли я був на ньому, оскільки люди співають його похвали. У мене немає нічого майже розміру використовуваного тестового файлу; найкраще, що я міг знайти в найкоротші терміни, - це родовідний файл розміром 14 М (довгі рядки, розділені пробілом, трохи менше 12000 рядків).

Коротка версія: mapfile з'являється швидше, ніж метод вирізання, але повільніше, ніж все інше, тому я б назвав це дурдом. хвіст | 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

Використовуючи те, що згадували інші, я хотів, щоб це була швидкою та денді функцією в моїй башті.

Створіть файл: ~/.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'

Ви отримаєте 2-й рядок з файлу. -f3дає 3-й рядок.


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

2

Для друку n-го рядка за допомогою sed із змінною як номер рядка:

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

Тут прапор '-e' призначений для додавання скрипту до команди, яку потрібно виконати.


2
Двокрапка є синтаксичною помилкою і повинна бути крапкою з комою.
трійка

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


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

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

П

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.