Есть ли способ игнорировать строки заголовка при сортировке UNIX?


102

У меня есть файл с полем фиксированной ширины, который я пытаюсь отсортировать с помощью утилиты сортировки UNIX (в моем случае Cygwin).

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

Есть ли способ указать sort либо «пропустить первые две строки через несортированные», либо указать порядок, при котором строки с двоеточиями сортируются вверх - остальные строки всегда начинаются с 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

Для записи: пока я использую командную строку «sort -t \\ -k1.1,1.6 <файл>» [данные могут содержать пробелы, но никогда не будут содержать обратную косую черту]
Роб Гиллиам,

Ответы:


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

Скобки создают подоболочку, заключающую в себе стандартный вывод, чтобы вы могли передать его по конвейеру или перенаправить, как если бы он поступил из одной команды.


Спасибо; Я принимаю этот ответ, поскольку он кажется наиболее полным и кратким (и я понимаю, что он делает!) - хотя он должен быть «head -n 2» :-)
Роб Гиллиам

1
Спасибо, исправил "головную" часть.
BobS

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

Вероятно, вы могли бы собрать что-то, что вы используете catдля перенаправления stdin во временный файл, а затем запустить указанную выше команду для этого нового файла, но он начинает становиться достаточно уродливым, поэтому, вероятно, лучше использовать одно из решений на основе awk, приведенных в другие отзывы.
BobS

@DamienPollet: См Dave «s ответ .
Джонатан Леффлер,

66

Если вы не против использования 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) его достаточно коротко, чтобы его запомнить. ваш пример охватывает больше крайних случаев. :) Это лучший ответ. работает на трубах. нет awk.
признаться.

1
Хорошо, я исправил это, и кажется, что bash делает все возможное, чтобы это работало. В общем, если вы закодируете это на C или другом языке, это не сработает, потому что stdio будет читать больше, чем только первую строку заголовка. Если вы запустите его для файла, доступного для поиска, bash прочитает больший кусок (128 байт в моем тесте), а затем вернется к концу первой строки. Если вы запустите его на конвейере, bash будет читать по одному символу за раз, пока не пройдет конец строки.
Сэм Уоткинс

Ницца! Если вы просто хотите съесть заголовок, это еще проще запомнить:extract_data | (read; sort)
Джейсон Суарес

Это почти идеальный вариант, но вам нужно использовать «IFS = read» вместо «read», чтобы сохранить начальные и конечные пробелы.
Станислав Герман-Евтушенко

6
На мой взгляд, это должен быть принятый ответ. Простой, лаконичный и более гибкий, так как он также работает с данными, передаваемыми по конвейеру.
Павел I

13

В простых случаях 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 извлекает все данные со стандартного ввода, не оставляя данных для сортировки. Попробуйте изменить команду на cat your_data | (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, который помещает результат в стандартный вывод, что позволяет вам отправить результат через другие фильтры перед записью в файл, если это необходимо)
Роб Гиллиам,

1

Итак, вот функция bash, аргументы которой точно такие же, как sort. Вспомогательные файлы и трубы.

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, полученная из других ответов. Он обрабатывает как файлы, так и каналы. Первый аргумент - это имя файла или "-" для стандартного ввода. Остальные аргументы передаются на сортировку. Пара примеров:

$ 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 имя_файла.txt | sed 1d», когда «sed 1d <имя_файла.txt» или даже просто «sed 1d имя_файла.txt» имеет такой же эффект?
Роб Гиллиам,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.