#! /usr/bin/gawk -f # Last edited on 2025-06-17 09:30:28 by stolfi BEGIN { usage = ( \ "cat INFILE \\\n" \ " | insert_blank_lines.gawk -v fields='FIELDS' \\\n" \ " > OUTFILE " \ ); # The {FIELDS} argument should be a list of field indices (starting from 1), # separated by commas. # Reads records from {stdin} and inserts a blank line whenever any of those # fields change. Also inserts a blank line at the end, if the file was not empty. # Input lines that are blank or begin with "#" are just copied and # ignored. abort = -1; nlin = 0; # Total number of lines read. ndat = 0; # Number of lines that are not blank or comments. ngrp = 0; # Numbe of groups of lines with the same fields. gsub(/[, ]+/, " ", fields); gsub(/^[ ]+/, "", fields); gsub(/[ ]+$/, "", fields); nf = split(fields, fix); # {fix[1..nf]} are the indices of fields to watch. if (nf == 0) { arg_error(("empty {fields} argument")); } for (i = 1; i <= nf; i++) { kf = fix[i] + 0; if (kf < 1) { data_error(("invalid field index " kf "")); } } split("", ofld); # {ofld[1..nf]} are the values of the watched fields on prev data line. split("", cfld); # {ofld[1..nf]} are the values of the watched fields on current data line. } (abort >= 0) { exit abort; } // { nlin++; # Cleanup funny spaces: gsub(/[\011]/, " ", $0); gsub(/[\015]/, "", $0); } /^[ ]*([#]|$)/ { # Pass through blanks and comments: print; next; } // { for (i = 1; i <= nf; i++) { kf = fix[i]; if (kf < 1) { data_error(("field " kf " missing")); } if (kf > NF) { data_error(("field " kf " missing")); } cfld[i] = $(kf); } if (fields_changed(nf,ofld,cfld)) { printf "\n"; ngrp++; } for (i = 1; i <= nf; i++) { ofld[i] = cfld[i]; } print; ndat++; next; } END { if (ndat > 0) { printf "\n"; ngrp++; } } function fields_changed(nf,ofld,cfld, j) { # Returns true iff fields {ofld[1..nf]} and {cfld[1..nf]} differ. for (j = 1; j < nf; j++) { if (cfld[j] != ofld[j]) { return 1; } } rerurn 0; } function arg_error(msg) { printf "%s\n", msg > "/dev/stderr"; printf "usage: %s\n", usage > "/dev/stderr"; abort = 1; exit 1 } function data_error(msg) { printf "%s:%d: %s\n", FILENAME, FNR, msg > "/dev/stderr"; abort = 1; exit 1 }