A different approach from Quasímodo's explicit loop in sed
:
$ sed 'h; s/.*|//; x; s/|[^|]*$//; y/|/ /; G; y/\n/|/' file
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |406 RCO 301
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
For each line, this saves the line in the hold space with h
and then removes everything on the line up to and including the last |
. It then swaps in the original copy of the line and removes the last |
and everything after it.
The pattern space now contains the original first portion of the line, and the hold space contains the last portion of the line.
The first y///
command replaces all remaining |
with spaces. G
appends the hold space to the end of the pattern space with a newline character in-between. The second y///
command transforms that newline character to a |
, and we're done.
Doing a limited (fixed) number of s///
substitutions and using the faster y///
command when possible means that this runs quicker than the explicit loop variation (~2.3 seconds on 50 MiB data, compared to ~7.8 seconds on the same data with the loop, using GNU sed
on my machine).
Interestingly, using a back reference in the explicit loop variation, like both me and Isaac did, slows it down even more (~33s with Isaac's variation, and ~29s with mine (in comments), on the same data set and under the same conditions as above).
Using awk
, this almost replaces all |
delimiters except the last one by a space. "Almost" since it inserts a space before the last |
.
$ awk -F '|' 'BEGIN { OFS = " " } { $NF = "|" $NF; print }' file
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |406 RCO 301
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
It reads each line as a set of |
-delimited fields, prepends a |
character to the start of the last field, and prints the resulting record with spaces for field delimiters.
With taking the default behavior of awk
into account (space is the default output field delimiter, and the input field delimiter is available as FS
):
awk -F '|' '{ $NF = FS $NF; print }' file
or, slightly shorter, courtesy of @Isaac,
awk -F '|' '{ $NF = FS $NF }; 1' file
|
on the line? So the first one will also be the last one. Should that be removed or left alone?sed 'y/|/\n/;s/\n/|/8;y/\n/ /' file
can be used.|
is fixed, a nicely chosen combination ofcut
andpaste
would probably do it.