use warnings; use strict; # $sep needs to be some char guaranteed not to be in the section name # (not the most elegant solution, but this isn't meant to be) my $sep = "\n"; my $target_sec = "ObjectType1${sep}NestedObject"; my $target_param = "Param1"; my $replace_val = "Hello"; my ($secname,@sec_stack,$cur_sec); while () { next if /^\s*#/ || !/\S/; if ( /^ (\s*) (.+?) \s* = \s* (.+?) \s* $/x ) { die "section name without a following block" if defined $secname; my ($indent,$param,$val) = ($1,$2,$3); if ($cur_sec eq $target_sec && $param eq $target_param) { $_ = $indent.$param.' = '.$replace_val."\n" } } elsif ( /^ \s* (\w+) \s* $/x ) { die "two section names following one another" if defined $secname; $secname = $1; } elsif ( /^ \s* \{ \s* $/x ) { die "'{' without name" unless defined $secname; push @sec_stack, $secname; $cur_sec = join $sep, @sec_stack; $secname = undef; } elsif ( /^ \s* \} \s* $/x ) { die "section name without a following block" if defined $secname; die "'}' without '{'" unless @sec_stack; pop @sec_stack; $cur_sec = join $sep, @sec_stack; } else { die "Failed to parse '$_'" } print $_; } __DATA__ # comment ObjectType1 { Param1 = Foo NestedObject { Param1 = Bar } # just another comment } ObjectType2 { Param1 = Quz NestedObject { Param1 = Baz } }