Таблицы реформатов

У меня есть несколько таблиц ( table.txt ), которые были неправильно построены и представлены избыточными в результатах, а именно:

 YEAR MONTH DAY RES 1971 1 1 245 1971 1 2 587 ... 1971 12 31 685 1971 1 1 245 1971 1 2 587 ... 1971 12 31 685 1972 1 1 549 1972 1 2 746 ... 

Вместо этого я хотел бы:

 YEAR MONTH DAY RES 1971 1 1 245 1971 1 2 587 ... 1971 12 31 685 1972 1 1 549 1972 1 2 746 ... 

Поэтому проблема заключается в том, что результаты представлены дважды в таблице. Это означает (с предоставленным примером), что после «1971 года» я должен был ожидать года 1972 года, а не «1971 год». Есть ли способ удалить избыточные результаты с помощью sh / bash?

Я должен заметить, что мои данные работают в течение 1971 года до 2099 года, и что они имеют точно такой же формат даже после 2000 года:

 YEAR MONTH DAY RES 1971 1 1 245 1971 1 2 587 ... 2000 1 1 875 2000 1 2 456 ... 2099 12 31 321 

Вот два взаимоисключающих цикла sed :

 sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn' <<"" YEAR MONTH DAY RES 1971 1 1 245 1971 1 2 587 ... 1971 12 31 685 1971 1 1 245 1971 1 2 587 ... 1971 12 31 685 1972 1 1 549 1972 1 2 746 ... 1972 12 31 999 1972 1 1 933 1972 1 2 837 ... 1972 12 31 343 

 YEAR MONTH DAY RES 1971 1 1 245 1971 1 2 587 ... 1971 12 31 685 1972 1 1 549 1972 1 2 746 ... 1972 12 31 999 

В основном sed имеет два состояния – p rint и есть . В первом состоянии – состояние p rint – sed автоматически устанавливает каждую строку ввода, затем проверяет ее на / 12 * 31 / pattern. Если текущее пространство шаблонов ! не совпадают, это d eleted и sed тянет в следующей строке ввода и снова запускает скрипт сверху – в команде p rint, не пытаясь запускать что-либо, что следует за командой d elete.

Если строка ввода соответствует / 12 * 31 / , то sed попадает во вторую половину скрипта – цикл питания . Сначала он определяет ветвь : label с именем n ; то он перезаписывает текущее пространство шаблонов с помощью строки ввода n ext, а затем сравнивает текущее пространство шаблона с // последним совпадающим шаблоном. Поскольку строка, которая соответствовала ей раньше, только что была перезаписана с помощью n ext, первая итерация этого цикла питания не соответствует, и каждый раз, когда это происходит ! not sed b обращается к метке :n чтобы получить строку ввода n ext и еще раз сравнить ее с // последним сопоставленным шаблоном.

Когда наконец будет завершено другое совпадение – примерно 365 n ext строк позже – sed делает -n автоматически распечатывать его, когда он завершает свой скрипт, тянет в следующую строку ввода и снова запускается сверху p помощью команды p rint в ее первом состоянии , Таким образом, каждое состояние цикла переходит к следующему на одном и том же ключе и делает как можно меньше, тем временем, чтобы найти следующий ключ.

Обратите внимание, что весь скрипт завершается без вызова единственной процедуры редактирования и что ему нужно только скомпилировать одно регулярное выражение. Полученный автомат очень прост – он понимает только [123 ] и [^123 ] . Более того, по крайней мере половина сравнений, скорее всего, будет сделана без компиляций, потому что единственный адрес, на который ссылается в цикле еды, является // пустым. Поэтому sed может завершить этот цикл полностью с помощью одного вызова regexec() каждой строки ввода. sed может делать аналогичные для петли p rint.


приуроченная


Мне было любопытно, как здесь могут выполняться различные ответы, и поэтому я придумал свой собственный стол:

 dash <<"" d=0 D=31 IFS=: set 1970 1 while case "$*:${d#$D}" in (*[!:]) ;; ($(($1^($1%4)|(d=0))):1:) D=29 set $1 2;; (*:1:) D=28 set $1 2;; (*[3580]:) D=30 set $1 $(($2+1));; (*:) D=31 set $(($1+!(t<730||(t=0)))) $(($2%12+1)) esac do printf '%-6d%-4d%-4d%d\n' "$@" $((d+=1)) $((t+=1)) done| head -n1000054 >/tmp/dates 

 dash <<<'' 6.62s user 6.95s system 166% cpu 8.156 total 

Это ставит миллион + строк в /tmp/dates date и удваивает выход за каждый из лет 1970 – 3338. Файл выглядит так:

 tail -n1465 </tmp/dates | head; echo; tail </tmp/dates 

 3336 12 27 728 3336 12 28 729 3336 12 29 730 3336 12 30 731 3336 12 31 732 3337 1 1 1 3337 1 2 2 3337 1 3 3 3337 1 4 4 3337 1 5 5 3338 12 22 721 3338 12 23 722 3338 12 24 723 3338 12 25 724 3338 12 26 725 3338 12 27 726 3338 12 28 727 3338 12 29 728 3338 12 30 729 3338 12 31 730 

… некоторые из них в любом случае.

И затем я попробовал разные команды:

 for cmd in "sort -uVk1,3" \ "sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn'" \ "awk '"'{u=$1 $2 $3 $4;if (!a[u]++) print;}'\' do eval "time ($cmd|wc -l)" </tmp/dates done 

 500027 ( sort -uVk1,3 | wc -l; ) \ 1.85s user 0.11s system 280% cpu 0.698 total 500027 ( sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn' | wc -l; ) \ 0.64s user 0.09s system 110% cpu 0.659 total 500027 ( awk '{u=$1 $2 $3 $4;if (!a[u]++) print;}' | wc -l; ) \ 1.46s user 0.15s system 104% cpu 1.536 total 

Команды sort и sed выполненные менее чем за половину времени awk , – и эти результаты были типичными. Я запускал их несколько раз. Кажется, что все команды также выписывают правильное количество строк – и поэтому они, вероятно, все работают.

sort и sed были довольно хорошо шеей и шеей – с sed как правило, впереди волос – для завершения времени для каждого прогона, но sort делает более актуальную работу для достижения своих результатов, чем любая из двух других команд. Он выполняет параллельные задания для выполнения своей задачи и многого помогает в моем многоядерном процессоре. awk и sed оба привязывают одноядерные, назначенные им в течение всего времени их обработки.

Результаты здесь взяты из стандартного, современного GNU sed , но я попробовал другое. Фактически, я пробовал все три команды с другими двоичными файлами, но только команда sed действительно работала с моими реликвиями. Остальные, как я полагаю, из-за нестандартного синтаксиса, просто уходят с ошибкой, прежде чем выйти из-под земли.

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

 PATH=/usr/heirloom/bin/posix2001:$PATH; time ... 

 500027 ( sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn' | wc -l; ) \ 0.31s user 0.12s system 136% cpu 0.318 total 

попробуйте подключить его к awk

 awk '!a[$0]++' files.txt > new_files.txt mv new_files.txt files.txt 

это будет выводить строку только один раз.

edit: (не уверен, что конкатенация var сделает трюк)

 awk '{u=$1 $2 $3 $4 ; if ( !a[u]++ ) print ; } ' ... 
 $ (head -1 table.txt ; tail -n +2 table.txt | sort -u -V -k1,3) YEAR MONTH DAY RES 1971 1 1 245 1971 1 2 587 1971 2 1 587 1971 12 31 685 1972 1 1 549 1972 1 2 746 2000 1 1 875 2000 1 2 456 2099 12 31 321