2009-10-27 1 views
1

У меня есть входной файл скажем, такие, как:Извлечение значения имя конкретного столбца с использованием патч в/AWK/Perl

a=1 b=2 c=3 d=4 
a=2 b=3 
a=0 c=7 
a=3 b=9 c=0 d=5 
a=4 d=1 
c=9 

Предположим, что порядок имен столбцов (а, б, в и г) остается таким же, , Как написать скрипт/команду, которая поможет мне извлечь значения, специфичные для столбцов b и d? Так что мой вывод должен быть:

b=2 d=4 
b=3 

b=9 d=5 
d=1 

Я мог бы написать «не так хорошо» команду AWK с помощью нескольких разделителей, чтобы фильтровать их, используя трубы использовать -F вариант, но я уверен, что есть более элегантный способ сделать это тоже.

Просьба помочь.

ответ

1

Sed сделает это довольно красиво:

 
sed -e 's/[^bd]=[^ ]*//g' -e 's/^ *//' -e 's/ *$//' < filename 

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

2

Вот версия однострочник:

 
$ perl -lpe '@x=/([bd]=[0-9])/g; $_="@x"' test.txt 

m//g в контексте списка возвращает все матчи в виде списка.

#!/usr/bin/perl 
use strict; use warnings; 

while (<DATA>) { 
    if(my @cols = /([bd]=[0-9])/g) { 
     print "@cols"; 
    } 
    print "\n"; 
} 

__DATA__ 
a=1 b=2 c=3 d=4 
a=2 b=3 
a=0 c=7 
a=3 b=9 c=0 d=5 
a=4 d=1 
c=9 

Выход:

 
C:\Temp> t.pl 
b=2 d=4 
b=3 

b=9 d=5 
d=1 
+0

Этот результат не тот, о котором он просил. – rsp

+0

@ rsp Да, я как-то пропустил образец вывода в первый раз. Теперь это исправлено. –

1

В Ruby:

#!/usr/bin/env ruby 
filename = ARGV[0] 
fields = ARGV[1..ARGV.length] 

File.open(filename) do |file| 
    file.each_line do |line| 
    pairs = line.split(' ').map { |expression| expression.split('=') } 
    value_hash = Hash[pairs] 

    requested_fields = [] 

    fields.each do |field| 
     requested_fields << "#{field}=#{value_hash[field]}" unless value_hash[field].nil? 
    end 

    puts requested_fields.join(' ') 
    end 
end 

вызовов с помощью ruby ruby_script_name.rb input_file.txt field1 field2.

Мне нравится, как коротко решение sed/perl - но насколько легко его можно изменить, чтобы получить более длинные имена полей? Похоже, что регулярное выражение скоро станет беспорядочным ... Во всяком случае, эта стратегия применима и здесь, если вы хотите ее использовать.

+0

'ruby -pe 'gsub (/ [^ bd] = \ d + * /," ")' file' – Telemachus

+0

Ruby может делать однострочные - даже если это не самое распространенное или предпочтительное использование для lang: http://www.fepus.net/ruby1line.txt – Telemachus

+0

Спасибо, Telemachus. Я буду использовать однострочники, но я обнаружил, что они имеют ограниченное применение в долгосрочной перспективе. То есть, я рад использовать их для информации, которую я знаю, будет использоваться только несколько раз и не нужно поддерживать. Я стараюсь использовать их больше всего в 'vim' (см.' Rubydo'). (На все, что требует кто-то другой, как правило, полагаются, так что плохо, когда вы возвращаетесь к нему через 3 месяца и не можете понять, почему цепочка из 10 регулярных выражений ломается. Я был там с моим кодом и другими люди, и это не весело.) В зависимости от того, что может понадобиться, может быть полезно. –

3
# awk '{ for(i=1;i<=NF;i++){if($i~/(b|d)=/){printf $i" "} }print ""}' file 
b=2 d=4 
b=3 

b=9 d=5 
d=1 
1

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

#! /usr/bin/env perl 
use warnings; 
use strict; 

my @lines; 

while(<>){ 
    my %kv = /([a-z])=([0-9])/ig; 
    push @lines, \%kv; 
} 

for my $kv (@lines){ 
    # $kv->{a} ||= 1; 
    # next unless $kv->{c}; 

    print "b=$kv->{b} " if defined $kv->{b}; 
    print "b=$kv->{d} " if defined $kv->{d}; 
    print "\n"; 
} 
+0

@Brad Я рад, что у кого-то была такая же идея (см. первая версия моего сообщения, которая была закрыта, пока я расширялся). +1. Обратите внимание, что вы должны использовать 'if defined $ kv -> {b}', потому что '0' является допустимым значением. –

0

Очевидно, что PostScript является путь ... XD

(%stdin) (r) file 
{ 
    dup 100 string readline not {exit} if 
    { 
     dup() eq {pop exit} if 
     token pop 3 string cvs 
     dup 0 get << 98/100/>> exch known 
     {print () print} {pop} ifelse 
    } loop 
    /= 
} loop 

Использование: gs -q -dNOPROMPT -dNODISPLAY -dBATCH thisfile.ps < input

Примечания: Заменить << 98/100/>> с соответствующими значениями ASCII (98 = Ь, 100 = d), за которым следует косой чертой с разделителями (хотя вам не нужно использовать косую черту, это просто фиктивный объект). Например, для выбора 'c', 'e' и 'f' используйте << 99/101/102/>>

Каждая строка может содержать не более 100 символов; если ваши линии более длинны, замените номер 100 string на некоторое большее число.Аналогичным образом замените номер 3 string, если ваши записи x=# длиннее трех символов. Это не работает, если x больше одного символа.