Чи є спосіб ігнорувати рядки заголовків у своєму роді UNIX?


102

У мене є файл із фіксованою шириною поля, який я намагаюся сортувати за допомогою утиліти сортування UNIX (у моєму випадку Cygwin).

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

Чи є спосіб сказати сортування або "пропустити перші два рядки через несортовані", або вказати впорядкування, яке сортує лінії двокрапки вгорі - решта завжди починаються з 6-значний цифр (що насправді є ключем I сортую), якщо це допоможе.

Приклад:

:0:12345
:1:6:2:3:8:4:2
010005TSTDOG_FOOD01
500123TSTMY_RADAR00
222334NOTALINEOUT01
477821USASHUTTLES21
325611LVEANOTHERS00

слід сортувати:

:0:12345
:1:6:2:3:8:4:2
010005TSTDOG_FOOD01
222334NOTALINEOUT01
325611LVEANOTHERS00
477821USASHUTTLES21
500123TSTMY_RADAR00

Для запису: командний рядок, який я використовую дотепер, - "сортувати -t \\ -k1.1,1.6 <файл>" [дані можуть містити пробіли, але ніколи не матимуть зворотної риски]
Роб Гілліам,

Відповіді:


125
(head -n 2 <file> && tail -n +3 <file> | sort) > newfile

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


Дякую; Я приймаю цю відповідь, бо вона видається найбільш повною і стислою (і я розумію, що це робить!) - це повинно бути "голова -n 2", хоча :-)
Роб Гілліам

1
Спасибі, виправили «головну» частину.
BobS

4
Чи існує можливість, щоб ця версія працювала над трубопровідними даними? Я спробував tee >(head -n $header_size) | tail -n +$header_size | sort, але голова, здається, біжить за tail|sortтрубою, тому заголовок закінчується надрукованим наприкінці. Це детермінований або расовий стан?
Дамієн Поллет

Ви, ймовірно, можете скласти щось, де ви використовуєте catдля перенаправлення stdin до тимчасового файлу, а потім запустіть вищевказану команду в цьому новому файлі, але він починає отримувати досить некрасиво, що, ймовірно, краще використовувати одне з заснованих на awk рішень, наведених у інші відповіді.
BobS


63

Якщо ви не заперечуєте проти використання awk, ви можете скористатися awkвбудованими можливостями труби

напр.

extract_data | awk 'NR<3{print $0;next}{print $0| "sort -r"}' 

Це друкує перші два рядки дослівно, а решта проходить наскрізь sort.

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


2
Дуже приємно, і це працює з довільними трубами, не тільки файлами!
lapo

4
Красиво, awk ніколи не перестає мене дивувати. Крім того, вам не потрібно $0, printдостатньо.
nachocab

1
@SamWatkins freeseek в відповідь менш некрасиво.
фіс.

Який варіант -r робить для сортування? Це повинен бути зворотний сорт?
гвроча

32

Ось версія, яка працює над трубопровідними даними:

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

Якщо ваш заголовок містить кілька рядків:

(for i in $(seq $HEADER_ROWS); do read -r; printf "%s\n" "$REPLY"; done; sort)

Це рішення звідси


9
приємно. для одиничного випадку заголовка я використовую extract_data | (read h; echo "$h"; sort) його досить коротко, щоб запам'ятати. ваш приклад охоплює більше крайових справ. :) Це найкраща відповідь. працює на трубах. ніякого пробудження.
фіс.

1
Гаразд, я розтягнув це і, здається, баш іде на особливу довжину, щоб зробити цю роботу. Загалом, якщо ви зашифрували це на C чи іншій мові, це не спрацювало, оскільки stdio прочитав би більше, ніж просто перший рядок заголовка. Якщо ви запускаєте його в шуканому файлі, bash зчитує більший фрагмент (128 байт у моєму тесті), а потім повертається до кінця першого рядка. Якщо запустити його на трубі, bash зчитує по одній знакові за один раз, поки не пройде кінець рядка.
Сем Уоткінс

Приємно! Якщо ви просто хочете з'їсти шапку, запам'ятати ще простіше:extract_data | (read; sort)
Джейсон Суарес

Це майже ідеально, але вам потрібно використовувати "IFS = read", а не "read", щоб зберегти провідні та відсталі місця.
Станіслав Німець-Євтушенко

6
Це, на мою думку, має бути прийнятою відповіддю. Простий, стислий та гнучкіший тим, що він також працює над трубопровідними даними.
Павло I

12

У простих випадках sedможна виконати роботу елегантно:

    your_script | (sed -u 1q; sort)

або рівнозначно,

    cat your_data | (sed -u 1q; sort)

Ключ знаходиться у 1q- надрукувати перший рядок (заголовок) і вийти (залишивши решту вводу дляsort ).

Наведений приклад: 2q зробить трюк.

-uПеремикач (небуферізованних) необхідний для тих , хто sedз ( в Зокрема, в GNU) , які могли б читати вхід на шматки, таким чином , що споживає дані , які ви хочете пройти sortзамість цього.


1
Привіт, @Andrea; Ласкаво просимо до Stack Overflow. Я боюся, що ваша відповідь не працює, принаймні, коли я тестую її в Git Bash для Windows (я перейшов із Cygwin, оболонки, якою я використовував іншу роботу 6 років тому). Команда sed витягує всі дані з stdin, не залишаючи даних для передачі для сортування. Спробуйте змінити команду, щоб визначити ваші_дані | (sed 1q; wc -l), щоб побачити, що я маю на увазі.
Роб Гілліам

1
Це може спрацювати, якщо ви вдруге передасте введення команді sed, наприклад: cat sortMe.csv | (sed 1q sortMe.csv; sort -t, -k3 -rn)> sorted.csv
Гаррі Креймер


4
head -2 <your_file> && nawk 'NR>2' <your_file> | sort

приклад:

> cat temp
10
8
1
2
3
4
5
> head -2 temp && nawk 'NR>2' temp | sort -r
10
8
5
4
3
2
1

3

Це займає лише 2 рядки коду ...

head -1 test.txt > a.tmp; 
tail -n+2 test.txt | sort -n >> a.tmp;

Для числових даних потрібно -n. Для сортування альфа -n не обов'язковий.

Приклад файлу:
$ cat test.txt

заголовок
8
5
100
1
-1

Результат:
$ cat a.tmp

заголовок
-1
1
5
8
100


1
Це в основному не та сама відповідь, як прийнята відповідь? (За винятком підходу BobS ставить результат на stdout, що дозволяє вам надсилати результат через інші фільтри, перш ніж записати їх у файл, якщо потрібно)
Rob Gilliam

1

Отже, ось функція bash, де аргументи точно схожі на сортування. Підтримка файлів і труб.

function skip_header_sort() {
    if [[ $# -gt 0 ]] && [[ -f ${@: -1} ]]; then
        local file=${@: -1}
        set -- "${@:1:$(($#-1))}"
    fi
    awk -vsargs="$*" 'NR<2{print; next}{print | "sort "sargs}' $file
}

Як це працює. Цей рядок перевіряє, чи є принаймні один аргумент та чи останній аргумент - файл.

    if [[ $# -gt 0 ]] && [[ -f ${@: -1} ]]; then

Це зберігає файл в окремий аргумент. Оскільки ми збираємося стерти останній аргумент.

        local file=${@: -1}

Тут ми видаляємо останній аргумент. Оскільки ми не хочемо передавати це як своєрідний аргумент.

        set -- "${@:1:$(($#-1))}"

Нарешті, ми робимо частину awk, передаючи аргументи (мінус останній аргумент, якщо це був файл), щоб сортувати в awk. Це було спочатку запропоновано Дейвом і модифіковано для того, щоб приймати подібні аргументи. Ми покладаємось на той факт, що $fileвін буде порожнім, якщо ми будемо трубопроводами, таким чином ігноруємо.

    awk -vsargs="$*" 'NR<2{print; next}{print | "sort "sargs}' $file

Приклад використання із відокремленим комою файлом.

$ cat /tmp/test
A,B,C
0,1,2
1,2,0
2,0,1

# SORT NUMERICALLY SECOND COLUMN
$ skip_header_sort -t, -nk2 /tmp/test
A,B,C
2,0,1
0,1,2
1,2,0

# SORT REVERSE NUMERICALLY THIRD COLUMN
$ cat /tmp/test | skip_header_sort -t, -nrk3
A,B,C
0,1,2
2,0,1
1,2,0

0

З Python:

import sys
HEADER_ROWS=2

for _ in range(HEADER_ROWS):
    sys.stdout.write(next(sys.stdin))
for row in sorted(sys.stdin):
    sys.stdout.write(row)

попередньо припускає, що в системі встановлений Python (у мене немає)
Роб Гілліам

0

Ось функція оболонки bash, отримана з інших відповідей. Він обробляє і файли, і труби. Перший аргумент - це ім'я файлу або "-" для stdin. Залишилися аргументи передаються для сортування. Кілька прикладів:

$ hsort myfile.txt
$ head -n 100 myfile.txt | hsort -
$ hsort myfile.txt -k 2,2 | head -n 20 | hsort - -r

Функція оболонки:

hsort ()
{
   if [ "$1" == "-h" ]; then
       echo "Sort a file or standard input, treating the first line as a header.";
       echo "The first argument is the file or '-' for standard input. Additional";
       echo "arguments to sort follow the first argument, including other files.";
       echo "File syntax : $ hsort file [sort-options] [file...]";
       echo "STDIN syntax: $ hsort - [sort-options] [file...]";
       return 0;
   elif [ -f "$1" ]; then
       local file=$1;
       shift;
       (head -n 1 $file && tail -n +2 $file | sort $*);
   elif [ "$1" == "-" ]; then
       shift;
       (read -r; printf "%s\n" "$REPLY"; sort $*);
   else
       >&2 echo "Error. File not found: $1";
       >&2 echo "Use either 'hsort <file> [sort-options]' or 'hsort - [sort-options]'";
       return 1 ;
   fi
}

0

Це те саме, що відповів Ян Шербін, але моя реалізація:

cut -d'|' -f3,4,7 $arg1 | uniq > filetmp.tc
head -1 filetmp.tc > file.tc;
tail -n+2 filetmp.tc | sort -t"|" -k2,2 >> file.tc;

-4
cat file_name.txt | sed 1d | sort 

Це зробить те, що ви хочете.


1) Це видаляє лише рядок заголовка і сортує решту, він не сортує все нижче рядка заголовка, залишаючи заголовок неушкодженим. 2) він видаляє лише перший рядок, коли заголовок насправді два рядки (читайте питання). 3) Чому ви використовуєте "cat file_name.txt | sed 1d", коли "sed 1d <file_name.txt" або навіть просто "sed 1d file_name.txt" має той же ефект?
Роб Гілліам
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.