Reputation: 11
I have an input file say, such as:
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
Assume that the order of column names (a,b, c and d) remains the same. How do I write a script/ command which will help me extract values specific to columns b and d? So my output should be:
b=2 d=4
b=3
b=9 d=5
d=1
I could write a "not-so-good" awk command using multiple delimiters to filter these out using pipes to use -F option, but I am sure there is a more elegant way to do this too.
Kindly help.
Upvotes: 1
Views: 3553
Reputation: 1062
Clearly, PostScript is the way to go ... 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
Usage: gs -q -dNOPROMPT -dNODISPLAY -dBATCH thisfile.ps < input
Notes: Replace the << 98 / 100 / >>
with the appropriate ASCII values (98 = b, 100 = d), each followed by a space-delimited slash (though you don't have to use the slash; it's just a dummy object). For example, to select 'c', 'e', and 'f', use << 99 / 101 / 102 / >>
Each line can be at most 100 characters; if your lines are longer replace the 100 string
with some larger number. Likewise, replace the 3 string
if your x=#
entries are longer than three characters. This doesn't work if the x is more than one character, though.
Upvotes: 0
Reputation: 34130
Assuming you may want to do the something to the values in the future, other than just filtering, you could use this as a basis.
#! /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";
}
Upvotes: 1
Reputation: 118166
Here is the one-liner version:
$ perl -lpe '@x=/([bd]=[0-9])/g; $_="@x"' test.txt
m//g
in list context returns all the matches as a list.
#!/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
Output:
C:\Temp> t.pl b=2 d=4 b=3 b=9 d=5 d=1
Upvotes: 2
Reputation: 343141
# 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
Upvotes: 3
Reputation: 12782
In 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
Call using ruby ruby_script_name.rb input_file.txt field1 field2
.
I like how short the sed
/perl
solution is -- but how easily can it be modified to take longer field names? Seems like the regex would become messy quickly... Anyway, that strategy would be applicable here as well, if you'd want to use it.
Upvotes: 1
Reputation: 99172
Sed will do it pretty nicely:
sed -e 's/[^bd]=[^ ]*//g' -e 's/^ *//' -e 's/ *$//' < filename
The first regex clears out the unwanted fields (everything except b and d), so that's where to modify it if you change your mind. The other two remove leading and trailing whitespace.
Upvotes: 1