Швидка команда Unix для відображення конкретних рядків у середині файлу?


206

Намагаюся налагодити проблему з сервером, і мій єдиний файл журналу - це файл журналу об'ємом 20 Гб (без часових позначок навіть! Чому люди використовують System.out.println()як журнал? У виробництві ?!)

Використовуючи grep, я знайшов область файлу, яку я хотів би подивитися, рядок 347340107.

Інше, ніж робити щось подібне

head -<$LINENUM + 10> filename | tail -20 

... для чого потрібно headбуде прочитати перші 347 мільйонів рядків файлу журналу, чи є швидка і проста команда, яка скидає рядки 347340100 - 347340200 (наприклад) на консоль?

оновлення Я повністю забув, що grep може надрукувати контекст навколо матчу ... це добре працює. Дякую!


Я думаю, що grep має шукати весь файл, для цього повинен бути менш інтенсивний процесор.
ojblass

Дивіться також stackoverflow.com/questions/6022384 / ...
flow2k

Відповіді:


69

з GNU-grep ви могли просто сказати

grep --context = 10 ...

7
Або точніше 10 рядків до: grep -B 10 ... Або 10 рядків після: grep -A 10 ...
Хлопчик Baukema

17
Ця команда не працює, нижче sed -n '<start>, <end> p' працює
Basav

5
Це насправді не те, що ви хочете, оскільки він обробить весь файл, навіть якщо відповідність знаходиться у верхньому біті. На даний момент комбо з головою / хвостом або хвостом / головою набагато ефективніше.
Склівз

3
Це взагалі не задовольняє задане питання, оскільки це не пропонує способу вивести певний рядок , як його задали.
Кріс Рассіс

1
Це насправді не те, що просили. @matt b, чому б ти не прийняв цю відповідь?
користувач1271772

390

Я знайшов два інші рішення, якщо ви знаєте номер рядка, але нічого іншого (жодна помилка неможлива):

Припускаючи, що вам потрібні лінії 20-40,

sed -n '20,40p;41q' file_name

або

awk 'FNR>=20 && FNR<=40' file_name

6
+1: Хоча ви можете вийти з друку після друку. Може запропонувати певні переваги, якщо файл дійсно величезний.
jaypal singh

awk 'NR> = 20 && NR <= 40'
file_name

2
sed -n '20, 40p; 41q 'ім'я_файлу для виходу потім.
Snigdha Batra

1
конкретно, це початкові та кінцеві номери рядків. Якщо ви знаходитесь у великому файлі, це буде «12345678,12345699p»
Кодекс руйнівника

1
Додатково до коментаря @ CodeAbominator 41qінструктуйте sed, щоб вийти за рядком 41.
Бріс

116
# print line number 52
sed -n '52p' # method 1
sed '52!d' # method 2
sed '52q;d' # method 3,  efficient on large files 

метод 3 ефективний для великих файлів

найшвидший спосіб відображення конкретних ліній


Я намагаюся розібратися, як адаптувати метод 3 до використання діапазону замість одного рядка, але я боюся, що мій sed-foo не вирішує завдання.
Xiong Chiamiov

9
@XiongChiamiov Як щодо sed -n '1,500p; 501q' для друку 1-500?
Сем

3
Причина, що перші два рядки / методи менш ефективні, полягає в тому, що вони продовжують обробляти всі рядки після рядка 52 до кінця, тоді як №3 припиняється після друку рядка 52.
flow2k

1
Ця відповідь допоможе пояснити, що всі аргументи роблять.
Брам Ванрой

25

Ні, немає, файли не адресовані рядком.

Немає способу пошуку початку рядка n у текстовому файлі постійного часу . Ви повинні пропустити файл і порахувати нові рядки.

Скористайтеся найпростішим / найшвидшим інструментом, який вам доведеться виконати. Для мене, використовуючи headробить набагато більше сенсу , ніж grep, так як останній спосіб складніший. Я не кажу " grepповільно", це насправді не так, але я був би здивований, якщо це швидше, ніж headу цьому випадку. Це, в headосновному, помилка .


2
Якщо рядки не мають фіксованої ширини в байтах, ви не знаєте, куди слід перемістити покажчик файлу, не рахуючи нових символів рядків з початку файлу.
Йосип Похоть

Це не дає відповіді на запитання. Щоб критикувати або вимагати роз'яснення у автора, залиште коментар під їх публікацією.
ексгума

@exhuma Ти маєш рацію. Я переписав. Сім років тому мене пом'якшили. :)
розмотує

20

А як на рахунок:

tail -n +347340107 filename | head -n 100

Я не перевіряв цього, але думаю, що це спрацює.


Ні, зазвичай хвостик має обмеження в 256 останніх кілобайт або подібне, залежно від версії та ОС.
Antti Rytsölä

💪 yessire miller
dctremblay

13

Я вважаю за краще просто зайти в lessта

  • ввівши, 50%щоб перейти до половини файлу,
  • 43210G перейти на лінію 43210
  • :43210 зробити те саме

і подібні речі.

Ще краще: натисніть, vщоб почати редагування (in vim, звичайно!), У цьому місці. Тепер зауважте, що vimтакі самі прив’язки ключів!


12

Я спершу розділив файл на кілька менших, як це

$ split --lines=50000 /path/to/large/file /path/to/output/file/prefix

а потім натисніть на отримані файли.


домовились, розірвіть цей журнал і створіть роботу cron, щоб це зробити правильно. використовуйте логротат або щось подібне, щоб вони не стали такими величезними.
Tanj

9

Ви можете використовувати exкоманду, стандартний редактор Unix (частина Vim зараз), наприклад

  • відобразити один рядок (наприклад, другий):

    ex +2p -scq file.txt

    відповідний синтаксис sed: sed -n '2p' file.txt

  • діапазон ліній (наприклад, 2-5 рядків):

    ex +2,5p -scq file.txt

    синтаксис sed: sed -n '2,5p' file.txt

  • від заданого рядка до кінця (наприклад, 5-й до кінця файлу):

    ex +5,p -scq file.txt

    синтаксис sed: sed -n '2,$p' file.txt

  • кілька діапазонів ліній (наприклад, 2-4 та 6-8 рядків):

    ex +2,4p +6,8p -scq file.txt

    синтаксис sed: sed -n '2,4p;6,8p' file.txt

Вище наведені команди можна перевірити за допомогою наступного тестового файлу:

seq 1 20 > file.txt

Пояснення:

  • +або -cпісля цього - виконайте команду (vi / vim) після того, як файл буде прочитаний,
  • -s - безшумний режим, також використовується поточний термінал як вихід за замовчуванням,
  • qДалі -cйде команда вийти з редактора (add !to do force quit, напр. -scq!).


6

Отримайте ack

Установка Ubuntu / Debian:

$ sudo apt-get install ack-grep

Потім запустіть:

$ ack --lines=$START-$END filename

Приклад:

$ ack --lines=10-20 filename

Від $ man ack:

--lines=NUM
    Only print line NUM of each file. Multiple lines can be given with multiple --lines options or as a comma separated list (--lines=3,5,7). --lines=4-7 also works. 
    The lines are always output in ascending order, no matter the order given on the command line.

1
Мені це здається командою з найінтуїтивнішим синтаксисом з усіх відповідей тут.
nzn

З версії 2.999_06 10 січня 2019 року --linesпараметр було видалено.
burny

4

sed потрібно буде також читати дані, щоб рахувати рядки. Єдиним способом, яким буде доступний ярлик, буде те, щоб у файлі існував контекст / порядок роботи. Наприклад, якщо були рядки журналу, попередньо встановлені з фіксованим часом / датою ширини тощо, ви можете використовувати утиліту look unix для двійкового пошуку через файли для конкретних дат / часу


4

Використовуйте

x=`cat -n <file> | grep <match> | awk '{print $1}'`

Тут ви отримаєте номер рядка, де відбувся матч.

Тепер ви можете використовувати наступну команду для друку 100 рядків

awk -v var="$x" 'NR>=var && NR<=var+100{print}' <file>

або ви можете також використовувати "sed"

sed -n "${x},${x+100}p" <file>

Якщо у вас більше одного матчу, використовуйте: "awk 'NR == 1 {print $ 1}" для першого матчу тощо
Рамана Редді

2

Коли sed -e '1,N d; M q'ви будете друкувати рядки N + 1 до M. Це, мабуть, трохи краще, grep -Cоскільки це не намагається зіставити рядки з візерунком.


-eтут необов’язково.
flow2k

2

Спираючись на відповідь Склівза, ось приємна функція, яку можна поставити у .bash_aliasesфайл. Це ефективно для величезних файлів при друкуванні матеріалів з передньої частини файлу.

function middle()
{
    startidx=$1
    len=$2
    endidx=$(($startidx+$len))
    filename=$3

    awk "FNR>=${startidx} && FNR<=${endidx} { print NR\" \"\$0 }; FNR>${endidx} { print \"END HERE\"; exit }" $filename
}

1

Для відображення рядки з <textfile>СВОЇХ <line#>, просто зробити це:

perl -wne 'print if $. == <line#>' <textfile>

Якщо ви хочете більш потужний спосіб показати діапазон рядків з регулярними виразами - я не скажу, чому grep - це погана ідея для цього, це має бути досить очевидним - цей простий вираз покаже вам ваш діапазон у один пропуск, який ви хочете мати при роботі з ~ 20 Гб текстовими файлами:

perl -wne 'print if m/<regex1>/ .. m/<regex2>/' <filename>

(підказка: якщо ваш регекс є /в ньому, використовуйте щось на зразок m!<regex>!)

Це буде роздруковуватись, <filename>починаючи з рядка, який збігається <regex1>до (і включаючи) рядка, який відповідає <regex2>.

Не знадобиться майстер, щоб побачити, як кілька налаштувань можуть зробити його ще більш потужним.

І останнє: perl, оскільки це зріла мова, має багато прихованих удосконалень, щоб сприяти швидкості та продуктивності. Зважаючи на це, це робить очевидним вибором для такої операції, оскільки вона спочатку була розроблена для обробки великих файлів журналів, тексту, баз даних тощо.


Дійсно, мені це не здається, оскільки коли одна команда perl є складнішою, ніж скажімо, запуск 2+ програм, з'єднаних разом (далі вниз по сторінці), і, я думаю, ви насправді говорите, тому що я набрав більше пояснення, яке вимагало ЧИТАТИ, оскільки на сторінці є однаково складна (або більше) сторінка, яка не зривається з води ... sheesh
osirisgothra

Зауважте, що користувач попросив діапазон рядків - ваш приклад можна тривіально адаптувати.
Склівз


0

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

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

1
Ви кажете, що це легко з awk, але ви це зробили замість perl?
В'язень 13

0

Я здивований, що лише одна відповідь (Рамана Редді) запропонувала додати номери рядків до виводу. Наступний пошук потрібного номера рядка та забарвлення виводу.

file=FILE
lineno=LINENO
wb="107"; bf="30;1"; rb="101"; yb="103"
cat -n ${file} | { GREP_COLORS="se=${wb};${bf}:cx=${wb};${bf}:ms=${rb};${bf}:sl=${yb};${bf}" grep --color -C 10 "^[[:space:]]\\+${lineno}[[:space:]]"; }

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