сортувати, але тримати рядок заголовка вгорі


55

Я отримую вихід з програми, яка спочатку створює один рядок, який є купою заголовків стовпців, а потім купу рядків даних. Я хочу вирізати різні стовпці цього виводу і переглянути його, відсортовані за різними стовпцями. Без заголовків різання та сортування легко здійснити за допомогою -kопції sortразом із cutабо awkпереглядати підмножину стовпців. Однак цей спосіб сортування змішує заголовки стовпців із рештою вихідних рядків. Чи є простий спосіб зберегти заголовки вгорі?


1
Я натрапив на таке посилання . Однак я не можу змусити цю техніку { head -1; sort; }працювати. Він завжди видаляє купу тексту після першого рядка. Хтось знає, чому це відбувається?
веселість

1
Я підозрюю, що це тому head, що читає більше одного рядка в буфер і викидає більшу частину його. Моя sedідея мала ту саму проблему.
Енді

@jonderry - ця техніка працює лише з lseekможливим введенням, тому вона не буде працювати при читанні з труби. Він спрацює, якщо ви переспрямовуєте файл >outfileі запускаєте його{ head -n 1; sort; } <outfile
don_crissti

Відповіді:


58

Вкрасти ідею Енді та зробити її функцією, щоб її було легше використовувати:

# print the header (the first line of input)
# and then run the specified command on the body (the rest of the input)
# use it in a pipeline, e.g. ps | body grep somepattern
body() {
    IFS= read -r header
    printf '%s\n' "$header"
    "$@"
}

Тепер я можу:

$ ps -o pid,comm | body sort -k2
  PID COMMAND
24759 bash
31276 bash
31032 less
31177 less
31020 man
31167 man
...

$ ps -o pid,comm | body grep less
  PID COMMAND
31032 less
31177 less

ps -C COMMANDможе бути більш доречним ніж grep COMMAND, але це лише приклад. Крім того, ви не можете використовувати, -Cякщо ви також використовували інший варіант вибору, наприклад -U.
Мікель

А може, це слід назвати body? Як і в body sortабо body grep. Думки?
Мікель

3
Перейменований з headerна body, тому що ви робите дію на тіло. Сподіваємось, це має більше сенсу.
Мікель

2
Не забудьте закликати bodyвсіх наступних учасників трубопроводу:ps -o pid,comm | body grep less | body sort -k1nr
єпископ

1
@ Тім Ви можете просто написати <foo body sort -k2або body sort -k2 <foo. Всього один зайвий персонаж із того, що ти хотів.
Мікель

36

Ви можете тримати заголовок вгорі таким чином за допомогою bash:

command | (read -r; printf "%s\n" "$REPLY"; sort)

Або зробіть це за допомогою perl:

command | perl -e 'print scalar (<>); print sort { ... } <>'

2
+1 дивним. Я думаю, що це стосується функції оболонки.
Мікель

1
+1, будь-яка причина, чому нижча оболонка є кращою, або це {}нормально замість ()?
депутат

2
IFS=вимикає розділення слів під час читання введення. Я не думаю, що це потрібно для читання $REPLY. echoрозширить скачки зворотної косої риби, якщо xpg_echoвстановлено (не за замовчуванням); printfв цьому випадку безпечніше. echo $REPLYбез лапок конденсується пробіл; Я думаю, echo "$REPLY"має бути гаразд. read -rпотрібен, якщо вхід може містити зворотні косої риски. Дещо з цього може залежати від версії bash.
Енді

1
@Andy: Вау, ви праві, різні правила для read REPLY; echo $REPLY(смужки провідних просторів) і read; echo $REPLY(ні).
Мікель

1
@Andy: IIRC, значення за замовчуванням xpg_echoзалежить від вашої системи, наприклад, від Solaris. Я думаю, що це значення за замовчуванням. Ось чому Жиллю printfтак подобається : це єдине з передбачуваною поведінкою.
Мікель

23

Я знайшов гарну версію awk, яка чудово працює у сценаріях:

awk 'NR == 1; NR > 1 {print $0 | "sort -n"}'

1
Мені це подобається, але це потребує трохи пояснень - труба знаходиться всередині сценарію awk. Як це працює? Це викликає sortкоманду зовнішньо? Хтось знає хоча б посилання на сторінку, що пояснює використання труби протягом awk?
Wildcard

@Wildcard ви можете перевірити офіційну сторінку керівництва або цей буквар .
лапо

4

Хакіш, але ефективний: додайте 0до всіх рядків заголовків та 1до всіх інших рядків перед сортуванням. Сортуйте перший символ після сортування.

… |
awk '{print (NR <= 2 ? "0 " : "1 ") $0}' |
sort -k 1 -k… |
cut -b 3-

3

Ось якийсь магічний шум перл-лінії, через який ви можете передавати свій вихід, щоб сортувати все, але тримати перший рядок у верхній частині: perl -e 'print scalar <>, sort <>;'


2

Я спробував command | {head -1; sort; }рішення і можу підтвердити, що він справді накручує речі - headчитається в декількох рядках з труби, потім виводиться лише перший. Отже, решта виводу, що head не було прочитано, передається до - sortНЕ решту результатів, починаючи з другого рядка!

Результат полягає в тому, що вам не вистачає рядків (і одного часткового рядка!), Які були на початку виведення команди (за винятком того, що у вас все ще є перший рядок) - факт, який легко підтвердити, додавши трубку wcв кінці вищезазначений трубопровід - але це надзвичайно важко простежити, якщо ви цього не знаєте! Я витратив щонайменше 20 хвилин, намагаючись розібратися, чому у мене з'явився частковий рядок (спочатку 100 байт або так відрізаний), перш ніж вирішити його.

Що я в кінцевому підсумку робив, що прекрасно працював і не вимагав виконання команди двічі, було:

myfile=$(mktemp)
whatever command you want to run > $myfile

head -1 $myfile
sed 1d $myfile | sort

rm $myfile

Якщо вам потрібно помістити висновок у файл, ви можете змінити це на:

myfile=$(mktemp)
whatever command you want to run > $myfile

head -1 $myfile > outputfile
sed 1d $myfile | sort >> outputfile

rm $myfile

Ви можете використовувати headвбудований файл ksh93 або lineутиліту (у системах, у яких все ще є) або, gnu-sed -u qабо IFS=read -r line; printf '%s\n' "$line"читайте вхід один байт за раз, щоб уникнути цього.
Стефан Шазелас

1

Я думаю, що це найпростіше.

ps -ef | ( head -n 1 ; sort )

або це можливо швидше, оскільки це не створює додаткової оболонки

ps -ef | { head -n 1 ; sort ; }

Інші класні використання

перетасувати рядки після рядка заголовка

cat file.txt |  ( head -n 1 ; shuf )

зворотні рядки після рядка заголовка

cat file.txt |  ( head -n 1 ; tac )

2
Див unix.stackexchange.com/questions/11856 / ... . Це насправді не гарне рішення.
Wildcard

1
Не працює, cat file | { head -n 1 ; sort ; } > file2тільки голова шоу
Пітер Краус

0
command | head -1; command | tail -n +2 | sort

4
Це починається commandдва рази. Тому він обмежений деякими конкретними командами. Однак для запитуваної psкоманди в прикладі вона буде працювати.
jofel

0

Просто і прямо!

<command> | head -n 1; <command> | sed 1d | sort <....>
  • sed nd ---> 'n' вказує номер рядка, а 'd' означає видалення.

1
Так само, як Джофел коментував рік-півтора тому відповідь Сарви, це починається commandдвічі. Тому не дуже підходить для використання в трубопроводі.
Wildcard

0

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

Щоб показати результати відсортовані, але із заголовками, які зберігаються вгорі (є 2 рядки заголовків), я вирішив:

w | head -n 2; w | tail -n +3 | sort

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

Зауважте, що tail -n +3засоби "показують усі рядки з 3-го попереду" (див. man tailПодробиці).


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