I have a flat file in UNIX format delimited by ; (Semicolon) with the following structure:
2020-01-30;1010;1
2020-01-30;1150;2
2020-02-03;1190;3
I must replace each character from position (12,4)
0->1
1->2
2->3
3->4 etc etc...
Staying:
2020-01-30;2121;1
2020-01-30;2261;2
2020-02-03;2201;3
I have tried several strategies with no success:
sed -i 's/0/9/g; s/1/8/g; s/2/7/g; s/3/6/g; s/4/5/g; s/5/4/g; s/6/3/g; s/7/2/g; s/8/1/g; s/9/0/g' data.dat > data1.dat
Do you have any strategy?
As the idea of being able to do it with , did not let me sleep
sed
, in the end I propose an answer to do the same.previous answer
With
awk
can be done in a concise way:What I do with
sprintf
is concatenate what it has$2
with the module 10 of the number plus 1. That way I make sure that it stays in the set of {0...9}.Define
OFS
how;
it is so that in the end, when I doprint $0
, I no longer have to separate it by;
andawk
do it for me.$0
has all the information of the record, but since we modify the variable$2
, then the information of the record is kept intact except for the second field$2
.The result is what you want:
Without the comments the code is:
Update
I recently took a crash course
sed
from Internet University and Reading the Manual University. And I managed to get the same thing but with the mere use ofsed
.Before putting the code with the explanation included, I think it is necessary to clarify certain fundamental concepts:
To communicate between these buffers we have the commands
h,H,g,G,x
. They copy or add from hold space to pattern space, or vice versa, or just swap what's in one for what's in another.This would explain it as if it were Samuel Beckett's book Molloy , where the tramp tries to suck every stone he has, using only the pockets (buffers) of his bags.
Once saying this, I put the code that should be saved in a file
sed_file.sed
with execution permissions given bychmod u+x sed_file.sed
:Or, without comment:
So we run:
Cuauhtli's answer is very good and solves the problem correctly.
Simply as a reference and to use other potentialities of GNU Awk, let's see how to do it in another way:
In GNU Awk it is possible to set the field separator to "nothing". With this, each character is a different field. This is done by indicating
-F ''
.Thus, for example, we can say:
Having learned this, it is now simply a matter of iterating over the string and modifying the characters from position 12 to 15, using the logic of Cuauhtli's answer:
That is, it iterates over all characters and prints the character itself (
$i
) unless it is in position 12 to 15, in which case we print($i+1)%10
. We make this decision using the ternary operatorcondición ? reacción_si_cierto : reacción_si_falso
.Finally we put
print $NF
to write the last field and that it be followed by a line break, becauseprintf
it does not add them automatically andprint
yes.You can do it with Perl, which comes by default in any Linux distribution.
Using the , options
n
,e
the following is executed for each line of input and is assumed to be in a loop, where the default variable ($_
) will fetch each line. With the optionp
it is printed at the end of each iteration (the content of the variable)So as a script what we really do is modify the content of this variable, which has the line.
First, we are going to "find" all the numbers that are in the second column. But we will be capturing them one by one, instead of the entire block. For each capture, we will replace its value with the "incremented"
Regular expression explanation:
(We will use the "g" modifier to do a global search)
To see this regular expression in action (without the replacements) you can go to this demo .
Finally we looked at what to replace it with.
In Perl, the modifier
e
on a regular expression (as in ) ,s/blabla/codigo/e
allows you to evaluate code.Since we have the number found in
$&
, what we do is add 1 to it and apply a modulus of 10 to it in order to go to 0 when we have a 10.