I am working with a csv file, so imagine I have this column:
5;10;>11;20;<14
My desired output would be:
5;10;12;20;13
So I would like to add 1 to those values who have the greater than (>) symbol and to subtract 1 to those values with a lesser than (<) symbol with bash language. I have tried something weird with sed but given that it interprets those changes as strings it didn't work out.
Any suggestions?
CodePudding user response:
With awk
(tested with GNU awk
):
$ awk -F\; -v OFS=\; '
{
for(i = 1; i <= NF; i ) {
if($i ~ /^<[[:digit:]] $/) {
sub(/^</,"",$i)
$i--
}
else if($i ~ /^>[[:digit:]] $/) {
sub(/^>/,"",$i)
$i
}
}
} 1' <<< "5;10;>11;20;<14"
5;10;12;20;13
Warning: use the following if and only if you trust your input file and you are 100% sure it does not contains malicious fields (see the final note).
With GNU sed
(and assuming your shell is bash
), a bit shorter but also a bit more difficult to understand (as usual with sed
):
$ sed -E '
s/<([[:digit:]] )/$((\1-1))/g
s/>([[:digit:]] )/$((\1 1))/g
s/.*/printf "%s\n" "&"/e
' <<< "5;10;>11;20;<14"
5;10;12;20;13
That is (where N
is a string of digits), substitute all <N
with $((N-1))
, all >N
with $((N 1))
, substitute the resulting string S
with printf "%s\n" "S"
, execute it with bash
and replace with the output (this is what the e
modifier of the substitute command does). In your example the input string successively becomes:
5;10;>11;20;$((14-1))
5;10;$((11 1));20;$((14-1))
printf "%s\n" "5;10;$((11 1));20;$((14-1))"
5;10;12;20;13
The reason why there is a serious security issue here is that if one of your fields is, for instance, $(rm -rf ~/*)
it will simply and recursively delete your entire home directory... So, if you do not control the input prefer the awk
version.
CodePudding user response:
5;10;>11;20;<14
|
{m,g}awk '
BEGIN {
_*=(OFS= "") (__-=_^= FS ="("(\
___="\31\17")"|"(____="\16\24")") "
} {
gsub(";[<>][0-9] ",____ "&" ___)
gsub(____ ";[<>]", "&" ___)
NF
for(_ =(_^=($_=$_)<"") _;_<=NF;_ ) {
if ($_~"^[0-9] $") {
$_ =__^($(_ __)~"[<]$")
}
} print $(_=_<_) }'
=
5;10;>12;20;<13