Справка по сценарию awk / sed shell

Я должен сделать скрипт, используя информацию в следующей таблице (фальшивая информация)

AnimalNumber,DOB,Gender,Breed,Date-moved-in IE161289240602,04/02/2010,M,AAX,20/07/2011, IE141424490333,13/01/2009,M,LMX,21/09/2010, IE151424420395,19/01/2007,F,LMX,20/08/2010, 

в основном мне нужно перечислить только DOB и animalnumber но число животных должно быть разбито так

IE161289240602 должен быть 1612892 4 0602

а также только месяц и год рождения, должны быть перечислены так что-то вроде этого для первой строки

 Feb 2010 1412892 4 0602 

Есть идеи, как это сделать ? Я боюсь, что это немного вне моего набора навыков

3 Solutions collect form web for “Справка по сценарию awk / sed shell”

Для GNU awk

 awk -F, ' NR>1{ sub("..","") #remove first two letters (mean IE) d="" for(i=split($2,D,"/");i>0;i--) #format 2nd field into `YY MM DD` d=d D[i] " " print strftime("%b %Y",mktime(d 0" "0" "0)),gensub("[0-9]"," & ",8,$1) }' file 
  • mktime создает mktime метку в секундах от EPOCH от строки в формате YYYY MM DD HH MM SS
  • strftime конвертирует strftime метку в желаемом формате (в случае %b %Y )
  • gensub заменяет 8 ю цифру ( [0-9] ) в 1-м поле ( $1 ) сам по себе ( & ) с конечными пробелами

Мы видим просто форматирование строк, поэтому можем использовать sed :

 sed -r ' 1d s/./ & /10 s|(../)(../)|\2\1| s/..([^,]*),([^,]*).*/date -d "\2" +"%b %Y \1"/e ' file 

или для sed без команды e

 sed ' 1d s/./ & /10 s|\(../\)\(../\)|\2\1| s/..\([^,]*\),\([^,]*\).*/date -d "\2" +"%b %Y \1"/ ' file | bash 

или

 sed ' s/./ & /10 s/../+"%b %Y / s/,/" -d / s|\(../\)\(../\)|\2\1| s/,/\n/ 1!P d' file | xargs -n3 date 

Я бы подумал «использовать perl»:

 #!/usr/bin/env perl use strict; use warnings; use Time::Piece; #get the column names out of the file. We remove the trailing linefeed. #<> is the magic input file handle, so it reads from STDIN or files #specified on command line, eg myscript.pl file_to_process.csv my @headers = split ( /,/, <> =~ s/\n//r ); while ( <> ) { chomp; #strip linefeed. my %stuff; #this makes use of the fact we know the headers already #so we can map from the line into named columns. @stuff{@headers} = split /,/; #read comma sep into hash #DOB: #take date, parse it into a unix time, then use strftime to output "Mon year" print Time::Piece -> strptime ( $stuff{'DOB'}, "%d/%m/%Y" ) -> strftime("%b %Y"); #regex match against AnimalNumber, and then join it with space separation. print "\t"; #separator print join ( " ", $stuff{'AnimalNumber'} =~ m/(\d+)(\d)(\d{4})/ ); print "\n"; } 

Эти результаты:

 Feb 2010 1612892 4 0602 Jan 2009 1414244 9 0333 Jan 2007 1514244 2 0395 

Это работает:

  • Чтение <> которое является маской дескриптором файла, принимает входные данные из труб или имен файлов.
  • Мы читаем первую строку и превращаем ее в массив @headers .
  • мы перебираем каждую дополнительную строку и сопоставляем значения, разделенные запятыми, в хэш (называемый %stuff ).
  • Извлеките DOB из %stuff – и обработайте его, используя strptime/strftime в дату, когда это необходимо.
  • извлеките AnimalNumber из %stuff и используйте шаблон регулярного выражения, чтобы извлечь числа, которые вы после
  • потому что мы используем несколько групп захвата, захваченные элементы возвращаются в виде списка, которые мы затем можем склеить (с разделителем пространства), используя join .

Изменить: поскольку вы смотрите на сортировку – сначала вам нужно сначала прочитать всю память в памяти (что выше не по соображениям эффективности).

Однако:

 #!/usr/bin/env perl use strict; use warnings; use Data::Dumper; use Time::Piece; my @headers = split( /,/, <> =~ s/\n//r ); my @records; while (<>) { chomp; #strip linefeed. my %stuff; #this makes use of the fact we know the headers already #so we can map from the line into named columns. @stuff{@headers} = split /,/; #read comma sep into hash #DOB: #take date, parse it into a unix time, then use strftime to output "Mon year" $stuff{'formtime'} = Time::Piece->strptime( $stuff{'DOB'}, "%d/%m/%Y" )->strftime("%b %Y"); #regex match against AnimalNumber, and then join it with space separation. #separator $stuff{'number_arr'} = [ $stuff{'AnimalNumber'} =~ m/(\d+)(\d)(\d{4})/ ]; push( @records, \%stuff ); } foreach my $record ( sort { $b->{'number_arr'}->[2] <=> $a->{'number_arr'}->[2] } @records ) { print join( "\t", $record->{'formtime'}, join( " ", @{ $record->{'number_arr'} } ), ), "\n"; } 

Как и выше, но мы предварительно обрабатываем каждую запись в массив хэшей, а затем используем sort вывода перед печатью – на основе поля «ключ» – последняя группа из 4 цифр в number_arr .

Другой способ Perl, использующий date GNU:

 $ perl -F, -lane 'next if $.==1; $F[0]=~s/IE(\d{7})(\d)(\d{4})/$1 $2 $3/; $F[1]=~s#(..).(..).(.*)#$2/$1/$3#; chomp($d=`date -d "$F[1]" +"%b %Y"`); print "$d $F[0]"' file Feb 2010 1612892 4 0602 Jan 2009 1414244 9 0333 Jan 2007 1514244 2 0395 

-a делает perl действовать как awk , разбивая свою строку ввода на символ, заданный -F и сохраняет его как массив @F . $F[0]=~s/IE... удаляет IE из первого поля и разделяет остальные по запросу. $F[1]=~s#... переформатирует дату в MM/DD/YYYY . Функция chomp(... будет запускать date GNU, попросив ее вернуть формат Mon YYYY ( chomp удаляет завершающие символы новой строки), который сохраняется как $d . Наконец, печатаются $d и измененное 1-е поле.

  • Как ускорить работу скрипта на основе поиска grep?
  • Как найти строки, содержащие более 100 символов, и содержит «if»?
  • Помощь с повторением поля A в CSV-файле, где поле B имеет указанное значение
  • Есть ли надежный инструмент командной строки для обработки CSV-файлов?
  • добавьте «#» в начале выбранных строк в файле
  • Вставить текст в определенные строки файла?
  • sed + удалить слово из определенной строки
  • Нежелательное совпадение с регулярным выражением SED (эмулировать perl's. *?)
  • Как удалить команды в истории, соответствующие заданной строке?
  • Декодирование кодировки URL (процентное кодирование)
  • Как я могу отображать 3 строки текста за один раз на 1 строку?
  • Linux и Unix - лучшая ОС в мире.