awk
These methods repeat for each pair of lines (1 and 2; 3 and 4; etc), working for as many #
characters as there are in the first line of each pair, and assuming that the two lines of each pair are the same length.
Compatible with GNU awk (Linux) and BSD awk (Mac).
Using substrings:
awk '{ a=$0 ; gsub(/#/,"",$0) ; print $0 ; getline ; for (n=1;n<=length(a);n++) if ( substr(a,n,1) != "#" ) printf "%s",substr($0,n,1) ; printf "%s",RS }' file.txt
The same code, reformatted for narrower screens:
awk '{
a=$0 ;
gsub(/#/,"",$0) ;
print $0 ;
getline ;
for (n=1;n<=length(a);n++)
if ( substr(a,n,1) != "#" )
printf "%s",substr($0,n,1) ;
printf "%s",RS
}' file.txt
a=$0
Save a copy of the first line.
gsub(/#/,"",$0) ; print $0
Delete all #
from the first line (not from the copy), then print the modified first line.
getline
Go to the next line.
for (n=1;n<=length(a);n++)
Step through each character of the first-line copy.
if ( substr(a,n,1) != "#" )
If this single-character substring is not #
,…
printf "%s",substr($0,n,1)
…then print the character from the corresponding position in the second line.
printf "%s",RS
End the second line with a newline character.
Using arrays:
awk '{ c=d="" ; elements=split($0,a,"") ; getline ; split($0,b,"") ; for (n=1;n<=elements;n++) if (a[n]!="#") { c = c a[n] ; d = d b[n] } ; print c ; print d }' file.txt
Reformatted for narrower screens:
awk '{
c=d="" ;
elements=split($0,a,"") ;
getline ;
split($0,b,"") ;
for (n=1;n<=elements;n++)
if (a[n]!="#")
{ c = c a[n] ; d = d b[n] } ;
print c ;
print d
}' file.txt
c=d=""
Initialize two blank strings. These will become modified versions of the two lines of input. This step is necessary if there are more than two lines of input.
elements=split($0,a,"")
Convert the first line of input into an array, with one character per array element. Store the number of array elements as the variable elements
.
getline
Go to the next line.
split($0,b,"")
Convert the second line of input into an array, with one character per array element.
for (n=1;n<=elements;n++)
Step through each element of the first-line array.
if (a[n]!="#")
If this single-character array element is not #
,…
{ c = c a[n] ; d = d b[n] }
…then, for each of the two lines, retain the character from position n
.
print c ; print d
Print the new versions of the two lines.
Caution: The Mac (BSD) version of awk does not automatically handle array elements in numerical order. This initially gave me surprising results.
The order in which a ‘for (indx in array)’ loop traverses an array is undefined in POSIX awk and varies among implementations. gawk lets you control the order by assigning special predefined values to PROCINFO["sorted_in"].
– The GNU Awk User’s Guide
The elements are still numbered 1,2,3,...
at the time of creation with split
, as in GNU awk, but BSD awk does not necessarily see them in that order when using for (n in array)
. Thus, you'll get scrambled gibberish.
To get around this, you can store an array's length (number of elements) when you create the array – eg, elements=split($0,a,"")
– and then iterate through the elements by using for (n=1;n<=elements;n++)
, as I've done here.
Example input (file.txt
):
abcdb#lae#blabl#a
abc~bola~xblabl~a
#alpha#beta#gamma#delta#epsilon#
abcdefghijklmnopqrstuvwxyzabcdef
Example output:
abcdblaeblabla
abc~bla~blabla
alphabetagammadeltaepsilon
bcdefhijkmnopqstuvwyzabcde