Команда для отримання n-го рядка STDOUT


210

Чи є якась команда bash, яка дозволить вам отримати n-й рядок STDOUT?

Тобто щось таке, що би сприйняло це

$ ls -l
-rw-r--r--@ 1 root  wheel my.txt
-rw-r--r--@ 1 root  wheel files.txt
-rw-r--r--@ 1 root  wheel here.txt

і робити щось подібне

$ ls -l | magic-command 2
-rw-r--r--@ 1 root  wheel files.txt

Я усвідомлюю, що це було б поганою практикою, коли писати сценарії, призначені для повторного використання, АЛЕ при роботі з оболонкою день у день, мені було б корисно мати можливість фільтрувати мою STDOUT таким чином.

Я також усвідомлюю, що це було б напівтривіальною командою для запису (буфер STDOUT, повернення певного рядка), але я хочу знати, чи є якась стандартна команда оболонки для цього, яка буде доступна, не будучи мені скрипт на місце.


5
Я щойно проголосував за те, щоб вживати словоmagic-command
Sevenearths

Відповіді:


332

Використання sed, лише для різноманітності:

ls -l | sed -n 2p

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

ls -l | sed -n -e '2{p;q}'

Я бачив це досить часто, що зазвичай використовую перший (який все одно простіше), хоча lsце не команда, яка скаржиться, коли отримує SIGPIPE.

Для діапазону ліній:

ls -l | sed -n 2,4p

Для декількох діапазонів ліній:

ls -l | sed -n -e 2,4p -e 20,30p
ls -l | sed -n -e '2,4p;20,30p'

Що означає p? як 2p 3p? У сенсі я розумію, що це на 2/3 рядка, але яка теорія / розуміння стоїть за ним?
Лев Уфімцев

4
@LeoUfimtsev: pце sedтвердження, яке друкує рядки, ідентифіковані діапазоном ліній, що передує йому. Таким чином, 2,4pозначає друковані рядки 2, 3, 4.
Джонатан Леффлер

3
І nозначає НЕ надрукувати всі інші рядки
Тимо

85
ls -l | head -2 | tail -1

13
+2, але я б припустив head -n 2 | tail -n 1- сучасні голови та хвости зазвичай ініціюють попередження.
Майкл Крелін - хакер

2
Використання двох процесів для фільтрації даних є дещо зайвим (але, враховуючи потужність машин сьогодні, вони, мабуть, справляться). Узагальнення для обробки одного діапазону не є абсолютно тривіальним (для діапазону N..M, який ви використовуєте head -n M | tail -n M-N+1), і узагальнення для обробки декількох діапазонів неможливо.
Джонатан Леффлер

3
голова забита в хвіст? жахливий. Кілька кращих способів це зробити.
camh

16
Чудова річ у командному рядку * nix: мільйон і один спосіб зробити все, і всі мають улюблене і ненавидять всі інші способи ... :-)
благає

1
Як друкувати діапазон рядків іншим способом: $ ls -l | awk '{if (NR>=4 && NR<=64) print}'(можна легше додати умови, кілька діапазонів тощо)
user10607

41

Альтернатива приємному способу голови / хвоста:

ls -al | awk 'NR==2'

або

ls -al | sed -n '2p'

1
мій awk краще, але sed приємно.
Майкл Крелін - хакер

Немає потреби у "якщо" - див. Відповідь хакера (крім частини про пошук кожного файлу в системі). ;-)
Призупинено до подальшого повідомлення.

що означає '2p'?
подрібнення

1
@shredding друк другого рядка з короткою відповіддю; long -> При використанні команді після адреси надаються лише рядки, які відповідають шаблону. Коротко, при використанні прапора / p прапорці друкуються двічі: файл sed '/ PATTERN / p'. І звичайно PATTERN - це будь-який регулярний вираз більше
Медхат

що якщо 2змінна? У кожному прикладі використовуються одинарні лапки, але для вар потрібні подвійні лапки. Чи можемо ми "програмувати" awk працювати також з одинарними цитатами при використанні vars? Приклад line =1; ls | awk 'NR==$line'. Я знаю, що bashвикористовує подвійні лапки з vars.
Тимо

21

З sed1line :

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

З awk1line :

# print line number 52
awk 'NR==52'
awk 'NR==52 {print;exit}'          # more efficient on large files

9

Заради повноти ;-)

коротший код

find / | awk NR==3

коротше життя

find / | awk 'NR==3 {print $0; exit}'

Ви не жартуєте про повноту!
Призупинено до подальшого повідомлення.

Я не ;-) Насправді, я не знаю, для чого всі користуються ls- початкове питання стосувалося когось STDOUT, тому я подумав, що краще мати його більше.
Майкл Крелін - хакер

Принаймні, для GNU awk дією за замовчуванням є { print $0 }, тому `awk 'NR == 3' - це коротший спосіб написати те саме.
ефемія

Ви, ймовірно, повинні додати "; exit" до цієї дії. Немає сенсу обробляти решту рядків, коли ви нічого не збираєтеся робити з ними.
camh

@camh: Дивіться відповідь Джонатана Леффера про це: "може генерувати SIGPIPE в процесі годування, що, в свою чергу, може генерувати небажане повідомлення про помилку".
ефеміент


6

Ви можете використовувати awk:

ls -l | awk 'NR==2'

Оновлення

Вищевказаний код не отримає те, що ми хочемо, через помилку, яка не є однією: перший рядок команди ls -l - це загальний рядок. Для цього буде працювати наступний переглянутий код:

ls -l | awk 'NR==3'

4

Чи легко Perl доступний для вас?

$ perl -n -e 'if ($. == 7) { print; exit(0); }'

Очевидно, замініть будь-яке число на 7.


Це моя прихильність, оскільки, здається, найпростіше узагальнити те, що я був особисто, після чого кожен n-й, perl -n -e 'if ($.% 7 == 0) {print; вихід (0); } '
мат kelcey

@matkelcey також можна зробити $ awk 'NR % 7' filenameдля друку кожного 7-го рядка у файлі.
user10607

3

Ще один плакат запропонував

ls -l | head -2 | tail -1

але якщо ви вставляєте голову в хвіст, схоже, все до лінії N обробляється двічі.

Забиваючи хвіст в голову

ls -l | tail -n +2 | head -n1

було б ефективніше?


0

Так, найефективніший спосіб (як уже вказував Джонатан Леффлер) - використовувати sed з print & quit:

set -o pipefail                        # cf. help set
time -p ls -l | sed -n -e '2{p;q;}'    # only print the second line & quit (on Mac OS X)
echo "$?: ${PIPESTATUS[*]}"            # cf. man bash | less -p 'PIPESTATUS'

Використання pipefail та PIPESTATUS дуже цікаво, і це багато чого додає цій сторінці.
Майк Ш

0

Хм

sed у моєму випадку не працював. Я пропоную:

для "непарних" рядків 1,3,5,7 ... ls | awk '0 == (NR + 1)% 2'

для "рівних" рядків 2,4,6,8 ls | awk '0 == (NR)% 2'


-1

Для більшої повноти ..

ls -l | (for ((x=0;x<2;x++)) ; do read ; done ; head -n1)

Відкиньте рядки, поки не дістанете до другого, а потім роздрукуйте перший рядок після цього. Отже, він друкує 3-й рядок.

Якщо це лише другий рядок ..

ls -l | (read; head -n1)

Покладіть стільки «прочитаних», скільки потрібно.


-1

Я додав це у свій .bash_profile

function gdf(){
  DELETE_FILE_PATH=$(git log --diff-filter=D --summary | grep delete | grep $1 | awk '{print $4}')
  SHA=$(git log --all -- $DELETE_FILE_PATH | grep commit | head -n 1 | awk '{print $2}')
  git show $SHA -- $DELETE_FILE_PATH
}

І це працює

gdf <file name>

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