Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have the following scenario and my lack of linux knowledge doesn't help a lot.

I have a text file that contains YML values, in the following matters:

  min: {z: -99613.0, y: 45.0, x: -99805.0}
  flags: {vehicle-place: allow}
  max: {z: 100387.0, y: 127.0, x: 100195.0}
  priority: 0
  type: cuboid
    groups: [jacob, eithan, michael]
    groups: [jack, noah]
      min: {z: 544.0, y: 6.0, x: 184.0}
  flags: {}
  max: {z: 556.0, y: 13.0, x: 197.0}
  priority: 0
  type: cuboid
    groups: [noah]
    groups: [logan, lucas, jack]

I want to generate files for each name with the regions that he belongs to.

For example noah.txt will contain coolregion, niceregion while jacob.txt will contain coolregion only.

I know regex pretty well so I'd be also pleased if you only point me to the right direction (i.e. a script that only needs the regex to complete).

If it matters, my linux version is "Debian GNU/Linux 5.0".

share|improve this question
up vote 5 down vote accepted

Here's an awk solution. I don't know YML, so you may need to fiddle with the regexps (e.g. can the region marker be indented?). Note that the print data >filename construct creates or truncates the file the first time it's reached for a given file name, and then appends to the file.

<input.yml awk '
/^[^ :]+: *$/ {sub(/: *$/,""); region=$0}     # start of region
/^ *groups:/ {                                # owner or member list
    sub(/^[^:]*: *\[/, ""); sub(/\].*/, "");  # extract bracketed names
    split($0, names, / *, */);                # split comma-separated list
    for (n in names)                          # iterate over names
        print region >names[n] ".txt";        # write or append to name file

Don't take it too seriously, but here's a sed and shell solution.

<input.yml sed -n -e '/^[^ ]/ h' \
                  -e '/^ \+groups:/ {' \
                    -e 'G' \
                    -e 's/^[^:]*: *\[\(.*\)\]\n\(.*\):/\2,\1/' \
                    -e 's/, \+/,/g' \
                    -e 'p' -e '}' | (
  IFS=,; set -f
  while read -r region names; do
    for name in $names; do
      echo "$region" >>"$name.txt"
share|improve this answer
Thanks a lot, it works lovely. The regions are indeed indent so I added 2 spaces before the [^ :]+: to make it work. YML is all about the right number of spaces :) – yellowblood May 8 '11 at 21:07

A 'perl' solution:


use warnings;                                                                                                                                                                       
use strict;                                                                                                                                                                         

die "Usage: perl $0 file\n" unless @ARGV == 1;                                                                                                                                      

my (%hash, $region);                                                                                                                                                                

open my $fh, "<", $ARGV[0] or die "Cannot open file $ARGV[0]: $!\n";                                                                                                                

while ( my $line = <$fh> ) {                                                                                                                                                        

        ## Get region, characters until first ':' without spaces at the beginning.                                                                                                  
        $region = $1 if $line =~ /^([^:\s]+)/;                                                                                                                                      

        ## Get names with a regex and save them as keys of a hash, values will be                                                                                                   
        ## regions.                                                                                                                                                                 
        if ( $line =~ /^\s*(?i:groups):\s*\[([^\]]*)\]\s*$/ ) {                                                                                                                     
                my @names = split /,\s*/, $1;                                                                                                                                       
                for my $name ( @names ) {                                                                                                                                           
                        push @{ $hash{ $name } }, $region;                                                                                                                          

## Read names (keys of the hash), open a file for each one and write regions on it.                                                                                                 
for my $name ( sort keys %hash ) {                                                                                                                                                  
        my $outfile = $name . ".txt";                                                                                                                                               
        open my $ofh, ">", $outfile or do { warn "Cannot open $outfile: $!\n"; next };                                                                                              
        print $ofh join( ", ", @{ $hash{ $name } } ), "\n";                                                                                                                         
        close $ofh or warn "Cannot close $outfile\n";                                                                                                                               

close $fh or warn "Cannot close $ARGV[0]\n";


$ perl script.pl infile
share|improve this answer
If you're using Perl, you can fairly easily use an actual YAML parser, which would add some robustness. – Tom Anderson Jun 29 '11 at 17:19

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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