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


101

Я пытаюсь получить конкретную строку из текстового файла.

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

cat file | while read line
    do
       #do something
    done

Я знаю, как перебирать строки, как показано выше, но что, если мне просто нужно получить содержимое определенной строки


ты знаешь номер строки?
Мехул Ратод,

1
Тогда вы можете считать.
Игнасио Васкес-Абрамс,

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

3
Почему catнормально, а sedнет? Это не имеет смысла.
Уильям Перселл,

5
Потому что никто не может сказать «нет» cat. Ой ... мило cat!

Ответы:


205

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снова, на этот вопрос, я согласен с вашим мнением.
Кент

22

Предположим, 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 Я сделал независимое предложение по редактированию на основе вашего комментария и согласен с ним. Надеюсь, на этот раз он не будет отклонен.
Виктор Заманян

16

Вы могли бы использовать sed -n 5p file.

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


11

Лучший метод производительности

sed '5q;d' file

Потому что sedперестает читать любые строки после пятой

Обновленный эксперимент от г-на Роджера Дуэка

Я установил wcanadian-insane (6,6 МБ) и сравнил 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-й.
Энтони Геохеган,

2
Я установил wcanadian-insane (6,6 МБ), сравнил sed -n 1p /usr/share/dict/wordsи sed '1q;d' /usr/share/dict/wordsиспользовал timeкоманду; первый занял 0,043 с, второй - всего 0,002 с, так что использование q определенно улучшает производительность!
Roger Dueck 04

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к команде чтения. (Что, вероятно, является желаемым поведением.)


1
$((++i))кажется башизмом; если OP ограничен в использовании внешних инструментов, я бы не предполагал, что у них будет доступ к чему-то большему, чем простой/bin/sh
Джосип Родин

@JosipRodin Нет, это функция POSIX (но поддержка ++приращений специально отмечена как необязательная).
Tripleee

@tripleee он не работает с современным тире как / bin / sh, поэтому я бы не стал на него полагаться.
Иосип Роден

Но простой обходной путь, например, $((i+=1))работает и в Dash.
Tripleee

$(($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 является экспериментальным, и его использование не рекомендуется
Сорин

Ни одно из других решений не является столь кратким и не допускает такой гибкости. (Почему кажется, что все, что экономит время и облегчает жизнь, «не одобряется» «умными людьми»,
разве

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

3
не могли бы вы хоть немного описать, почему эта работа, чтобы было понятнее человеку, задавшему вопрос?
ted

Итак, первый grep выбирает все строки, добавляя номера строк в их начале. Затем второй grep выбирает конкретную строку, сопоставляя номер строки в начале. И, наконец, номер строки обрезается от начала строки в эхо.
Одер

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