Reputation: 406
I have a file of the following structure:
4 1:4 3:1 6:1 56:2 57:4 58:7 59:1 66:2
1 2:1 56:1 57:1 58:2 59:2 65:1
8 1:2 2:1 3:8 12:1 56:1 57:2 58:2 59:2 66:1 67:2
What I need to do is to switch the values associated with 56 and 57 to the ones associated with 58 and 59:
4 1:4 3:1 6:1 56:7 57:1 58:2 59:4 66:2
1 2:1 56:2 57:2 58:1 59:1 65:1
8 1:2 2:1 3:8 12:1 56:2 57:2 58:1 59:2 66:1 67:2
For now I am trying to perform the replacement with at least two columns using something like:
awk '{
for ( i=2;i<=NF;i++ )
{
split(, a, ":")
arr[a[1]] = a[2]
}
}
END {
n = asorti(arr, dest)
ORS=
for ( i=1; i<=n; i++ )
{
if ( dest[i] != 56 && dest[i] != 58 )
print dest[i] ":" arr[dest[i]]
else
{
if ( dest[i] == 56 )
print dest[i] ":" arr[dest[i+2]]
if ( dest[i] == 58 )
print dest[i] ":" arr[dest[i-2]]
}
}
}' file
However, that looks quite bulky and final indexes are not sorted properly. Will appreciate any other solution.
Upvotes: 0
Views: 52
Reputation: 74595
If you can rely on the ordering always being 56, 57, 58, 59, then you can do this with sed:
sed -E 's/(56:)([0-9]+)(.*57:)([0-9]+)(.*58:)([0-9]+)(.*59:)([0-9]+)/\1\6\3\8\5\2\7\4/' file
Just capture all the parts and reshuffle them in the replacement.
The odd-numbered capture groups refer to the "labels" and any leading content, except for in the first case where we don't have to touch that part of the line. The even-numbered ones refer to the numerical values.
Upvotes: 1
Reputation: 92854
GNU awk
solution:
awk '{
r=gensub(/\<(56:)([0-9]+) (57:)([0-9]+) (58:)([0-9]+) (59:)([0-9]+)/,
"\\1\\6 \\3\\8 \\5\\2 \\7\\4", "g");
print r
}' file
The output:
4 1:4 3:1 6:1 56:7 57:1 58:2 59:4 66:2
1 2:1 56:2 57:2 58:1 59:1 65:1
8 1:2 2:1 3:8 12:1 56:2 57:2 58:1 59:2 66:1 67:2
gensub(regexp, replacement, how [, target])
Search the target string
target
for matches of the regular expressionregexp
. Ifhow
is a string beginning with ‘g
’ or ‘G
’ (short for “global”), then replace all matches ofregexp
withreplacement
.
Upvotes: 1