Отримайте конкретний рядок з текстового файлу, використовуючи лише скрипт оболонки


100

Я намагаюся отримати певний рядок із текстового файлу.

Поки в Інтернеті я бачив лише такі речі, як sed, (я можу використовувати лише sh -not bash або sed або щось подібне). Мені потрібно це робити лише за допомогою базового сценарію оболонки.

cat file | while read line
    do
       #do something
    done

Я знаю, як перебирати рядки, як показано вище, але що робити, якщо мені просто потрібно отримати вміст певного рядка


чи знаєте ви номер рядка?
Мехул Ратод

1
Тоді ви отримуєте рахувати.
Ігнасіо Васкес-Абрамс

так, номер рядка 5 @MehulRathod
GangstaGraham

3
Чому це catдобре, але sedні? В цьому немає сенсу.
Вільям Перселл

5
Тому що ніхто не може сказати "ні" cat. Ау ... мило cat!

Відповіді:


204

sed:

sed '5!d' file

awk:

awk 'NR==5' file

Що з командою sh, я не можу використовувати sed, awk. Я повинен зробити це більш зрозумілим у питанні.
GangstaGraham

@GangstaGraham Ви сказали, що знаєте, як перебрати рядки, як щодо додавання лічильника? якщо лічильник досягне вашого цільового номера рядка, дістаньте лінію та розірвіть цикл. чи допомагає це?
Кент

4
@KanagaveluSugumar прочитав інформацію про сторінку sed. 5!dозначає видалити всі рядки, крім 5. оболонки var можливо, вам потрібні подвійні лапки.
Кент

13
Я б запропонував додати ще один варіант: sed -n 5pдля новачків це здається більш логічним, оскільки -nозначає "відсутність результатів за замовчуванням" і pозначає "друк", і немає потенційно заплутаної згадки про видалення (коли люди говорять про файли, видалення рядків має тенденцію до означають щось інше).
Йосип Родін

1
@JosipRodin ви праві, -n '5p'працює і для цієї проблеми. Різниця тут полягає в тому, що 5!dви можете додати, -iщоб записати зміни у файл. однак, з -n 5pвами sed -n '5p' f > f2&& mv f2 fзнову, з цього питання, я згоден з вашою думкою.
Кент

21

Якщо припустити line, це змінна, яка містить потрібний номер рядка, якщо ви можете використовувати headі tail, то це досить просто:

head -n $line file | tail -1

Якщо ні, це має працювати:

x=0
want=5
cat lines | while read line; do
  x=$(( x+1 ))
  if [ $x -eq "$want" ]; then
    echo $line
    break
  fi
done

Це -eqпорівняння призначене для цілих чисел, тому потрібно номер рядка, а не вміст рядка ( $line). Це потрібно виправити, визначаючи, наприклад, want=5перед циклом, а потім використовуючи -eqпорівняння на $want. [перейшов з відхиленої редакції]
Йосип Родін

1
@JosipRodin Я зробив незалежну пропозицію щодо редагування на основі вашого коментаря, оскільки я згоден з цим. Сподіваємось, цього разу він не буде відхилений.
Віктор Заманян

15

Ви можете використовувати sed -n 5p file .

Ви також можете отримати діапазон, наприклад, sed -n 5,10p file.


11

Найкращий метод виконання

sed '5q;d' file

Тому що sedперестає читати будь-які рядки після 5-го

Оновіть експеримент від містера Роджера Дуека

Я встановив wcanadian-insane (6.6MB) і порівняв sed -n 1p / usr / share / dict / words і sed '1q; d' / usr / share / dict / words за допомогою команди time; перший займав 0,043, другий лише 0,002, тому використання 'q' безумовно, поліпшення продуктивності!


1
Про це також часто пишуть:sed -n 5q
Вільям Перселл

3
Мені подобається це рішення, тому що sedпісля 5-го перестає читати будь-які рядки.
Ентоні Геоґеган

1
Я встановив wcanadian-insane (6.6MB) і порівняв sed -n 1p /usr/share/dict/wordsта sed '1q;d' /usr/share/dict/wordsвикористовував timeкоманду; перший займав 0,043, другий лише 0,002, тому використання 'q' безумовно, поліпшення продуктивності!
Роджер

5

Якщо ви, наприклад, хочете отримати рядки від 10 до 20, ви можете використовувати кожен із цих двох методів:

head -n 20 york.txt | tail -11

або

sed -n '10,20p' york.txt 

p вище командні стенди для друку.

Ось що ви побачите: введіть тут опис зображення


2

Стандартний спосіб зробити подібну річ - це використання зовнішніх інструментів. Забороняти використання зовнішніх інструментів під час написання сценарію оболонки є абсурдом. Однак якщо ви дійсно не хочете використовувати зовнішні інструменти, ви можете надрукувати рядок 5 за допомогою:

i=0; while read line; do test $((++i)) = 5 && echo "$line"; done < input-file

Зауважте, що це надрукує логічний рядок 5. Тобто, якщо вони input-fileмістять продовження рядків, вони будуть рахуватися як один рядок. Ви можете змінити цю поведінку, додавши -rдо команди read. (Яка, мабуть, бажана поведінка.)


1
$((++i))виявляється башизмом; якщо ОП обмежена у використанні зовнішніх інструментів, я не припускаю, що вони матимуть доступ до більш ніж простого/bin/sh
Йосип Родін

@JosipRodin Ні, це функція POSIX (але підтримка ++приростів спеціально позначена як необов'язкова).
tripleee

@tripleee це не працює з сучасним тире як / bin / sh, тому я б не покладався на нього.
Йосип Родін

Але просте вирішення, як і $((i+=1))роботи в Dash, теж.
трійка

$(($i+1))це просте вирішення, про яке я думав.
Йосип Родін

1

Паралельно з відповіддю Вільяма Перселла , ось проста конструкція, яка повинна працювати навіть в оригінальній оболонці Bourne v7 (і, таким чином, також місцях, де Bash недоступний).

i=0
while read line; do
    i=`expr "$i" + 1`
    case $i in 5) echo "$line"; break;; esac
done <file

Зауважте також оптимізацію breakвиходу з циклу, коли ми отримали потрібну лінію.


0

Мені особливо не сподобалася відповідь.

Ось як я це зробив.

# Convert the file into an array of strings
lines=(`cat "foo.txt"`)

# Print out the lines via array index
echo "${lines[0]}"
echo "${lines[1]}"
echo "${lines[5]}"

-1

Легко за допомогою perl! Якщо ви хочете отримати рядки 1, 3 та 5 з файлу, скажіть / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd

seq 5 | perl -ne 'print if $. ~~ [1, 4, 5]'але smartmatch експериментальний, і його використання
Сорін

Ні одне з інших рішень не є таким стислим, або допускає велику гнучкість. (Чому здається, що все, що економить час і полегшує все, "відлякує" розумними людьми? Чи ми всі повинні дивитись на екрани цілий день?)
dagelf

-1
line=5; prep=`grep -ne ^ file.txt | grep -e ^$line:`; echo "${prep#$line:}"

3
Ви могли б хоч трохи описати, чому цей твір зробити його зрозумілішим для людини, яка поставила запитання?
тед

Отже, перший греп вибирає всі рядки, додаючи номери рядків на їх початку. Тоді другий греп вибирає конкретний рядок, порівнюючи номер рядка на початку. І нарешті номер рядка обрізається від початку рядка в ехо.
Одер

Це як складне, так і неефективне порівняно з тим sed -n 5p, що, звичайно, все ж можна оптимізувати до чогось подібногоsed -n '5!d;p;q'
tripleee
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.