3

I have a file delimited with space and random column ordering as follows:

name=Joan age=42 ip=172.20.1.80 sex=M loc=UK 
loc=IR sex=F ip=172.20.1.1 age=32 name=Sandra 

I want to extract specific fields (name, loc, and ip) only.

So the result that I'm looking for is as follows:

Joan|UK|172.20.1.80
Sandra|IR|172.20.1.1
2
  • Similar (if you just replace the space with newline), stackoverflow.com/questions/16571739/…
    – michael
    Jan 27, 2019 at 13:04
  • @michael Not quite. You need something to detect the different "blocks" then if the input has more that one line.
    – PerlDuck
    Jan 27, 2019 at 13:12

3 Answers 3

4

Luckily, your input file has a format the shell understands when it comes to assigning variables a value: var1=value1 var2=value2 etc. So we can simply read each line and use the eval command to evaluate the line.

Put the following into a file, say parse.sh, do chmod +x parse.sh and run it with your input file as a parameter.

Script parse.sh:

#!/usr/bin/env bash

while read line; do
    eval $line;
    echo "$name|$loc|$ip"
done < "$1"

exit 0;

File input.txt:

name=Joan age=42 ip=172.20.1.80 sex=M loc=UK
loc=IR sex=F ip=172.20.1.1 age=32 name=Sandra

Run:

me@ubuntu:~> ./parse.sh input.txt 
Joan|UK|172.20.1.80
Sandra|IR|172.20.1.1

Please note that the values must not have a space in them. E.g.

ip=... name=Ubai salih loc=...

would not work and give syntax errors. Also, if the input file would contain a line with a bad_command that command gets executed because that is how eval works: it just executes the given string.

4
  • 2
    Die to the risk if code injection (malicious or accidentally), I might prefer a different solution.
    – michael
    Jan 27, 2019 at 13:06
  • 1
    @michael Yes, you are right. I added a note to the answer to mention that. Another possibility would be to split at spaces and then at =. Or pick name=\S+ etc. with a regex. Feel invited to post another approach ;-)
    – PerlDuck
    Jan 27, 2019 at 13:09
  • 1
    @michael *Due to the risk *of ... Too late to edit the comment I know, but just wanted to mention the typos in case anyone else is confused.
    – wjandrea
    Jan 27, 2019 at 21:16
  • Oh goodness, sorry, I just noticed the typo (I blame autocorrect). Code injection is bad, but only on rare occasion lethal.
    – michael
    Jan 28, 2019 at 14:15
1

FWIW, here's a Python solution like PerlDuck's Bash solution, but not evaluating the input.

#!/usr/bin/env python3

import fileinput

for line in fileinput.input():
    record = line.rstrip('\n')
    d = dict(kv.split('=') for kv in record.split(' '))
    print(d['name'], d['loc'], d['ip'], sep='|')

Run:

$ ./parse.py input.txt
Joan|UK|172.20.1.80
Sandra|IR|172.20.1.1
0

Since the output order that you want is reverse lexical (name > loc > ip) you could select and then reverse sort the fields, then remove the fieldname= prefixes. For example in Perl:

$ perl -alne '
    print join "|", map { s/.*=//r } reverse sort grep { /^(name|loc|ip)=/ } @F
' file
Joan|UK|172.20.1.80
Sandra|IR|172.20.1.1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.