Команда для получения 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
Семерка

Ответы:

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'
Джонатан Леффлер
источник
Что означает р? как 2р 3р? В том смысле, что я понимаю, что это для 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
голова в хвосте? какой ужас. Несколько лучших способов сделать это.
Съемка
16
Отличная вещь в командной строке * nix: миллион и один способ сделать все, и у каждого есть любимый и ненависть ко всем остальным ... :-)
просят
1
Как напечатать диапазон строк другим способом: $ ls -l | awk '{if (NR>=4 && NR<=64) print}'(можно добавить больше условий, несколько диапазонов и т. Д.)
user10607
41

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

ls -al | awk 'NR==2'

или

ls -al | sed -n '2p'
ChristopheD
источник
1
мой awk лучше, но sed хорош.
Майкл Крелин - хакер
Нет необходимости в «если» - см. Ответ хакера (за исключением части о поиске каждого файла в системе). ;-)
Пауза до дальнейшего уведомления.
что означает '2p'?
измельчение
1
@shredding короткий ответ напечатать вторую строку; long -> При использовании только строки, соответствующие шаблону, получают команду после адреса. Вкратце, при использовании с флагом / p совпадающие строки печатаются дважды: файл sed '/ PATTERN / p'. И конечно ОБРАЗЕЦ любое регулярное выражение более
Medhat
что если 2переменная? В каждом примере используются одинарные кавычки, но для переменной var нужны двойные кавычки. Можем ли мы "запрограммировать" awk для работы с одинарными кавычками при использовании vars? Пример line =1; ls | awk 'NR==$line'. Я знаю, что bashиспользует двойные кавычки с переменными.
Тимо
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 - более короткий способ написать то же самое.
эпимент
Вы, вероятно, должны добавить «; выход» к этому действию. Нет смысла обрабатывать остальные строки, когда вы не собираетесь ничего с ними делать.
Съемка
@camh: См. ответ Джонатана Леффера об этом: «может генерировать SIGPIPE в процессе кормления, что, в свою очередь, может привести к появлению нежелательного сообщения об ошибке».
Эфимент
7

Попробуйте эту sedверсию:

ls -l | sed '2 ! d'

Он говорит: «удали все строки, которые не являются вторыми».

Приостановлено до дальнейшего уведомления.
источник
6

Вы можете использовать awk:

ls -l | awk 'NR==2'

Обновить

Приведенный выше код не получит то, что нам нужно, из-за ошибки off-by-one: первая строка команды 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); } '
Мат Келси
@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

Хмм

Сед не работал в моем случае. Я предлагаю:

для "нечетных" строк 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)

Положите столько прочитанных, сколько необходимо.

Shizzmo
источник
-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>
Джоэл АЗЕМАР
источник
Я озадачен - какое это имеет отношение к вопросу?
Джонатан Леффлер