I have a text file:
$cat ifile.txt
this is a text file
assign x to 9 and y to 10.0702
define f(x)=x+y
I would like to disable the original line and divide the x-value by 2 and multiply the y-value by 2
My desired output is
$cat ofile.txt
this is a text file
#assign x to 9 and y to 10.0702
assign x to 5 and y to 20.1404
define f(x)=x+y
Here 5 is calculate from 9/2 and rounded to the next integer
and 20.14 is calculated from 10.07x2 and not rounded
I am thinking of the following way, but can't write a script.
if [ line contains "assign x to" ]; then new_x_value=[next word]/2
if [ line contains "and y to" ]; then new_y_value=[next word]x2
if [ line contains "assign x to" ];
then disable it and add a line "assign x to new_x_value and y to new_y_value"
Would you please try the following:
#!/bin/bash
pat="(assign x to )([[:digit:]]+)( and y to )([[:digit:].]+)"
while IFS= read -r line; do
if [[ $line =~ $pat ]]; then
echo "#$line"
x2=$(echo "(${BASH_REMATCH[2]} + 1) / 2" | bc)
y2=$(echo "${BASH_REMATCH[4]} * 2" | bc)
echo "${BASH_REMATCH[1]}$x2${BASH_REMATCH[3]}$y2"
else
echo "$line"
fi
done < ifile.txt > ofile.txt
Output:
this is a text file
#assign x to 9 and y to 10.0702
assign x to 5 and y to 20.1404
define f(x)=x+y
- The regex
(assign x to )([[:digit:]]+)( and y to )([[:digit:].]+)
matches a literal string, followed by digits, followed by a literal string, and followed by digits including decimal point. - The
bc
command(${BASH_REMATCH[2]} + 1) / 2
caclulates theceiling
value of the input divided by 2. - The next
bc
command${BASH_REMATCH[4]} * 2
multiplies the input by 2.
The reason I have picked bash
is just because it supports back reference
in regex and is easier to parse and reuse the input parameters than awk
. As often pointed out, bash
is not suitable for processing large files due to the performance reason. If you plan to large / multiple files, it will be recommended to use other languages like perl
.
With perl
you can say:
perl -pe 's|(assign x to )([0-9]+)( and y to )([0-9.]+)|
"#$&\n" . $1 . int(($2 + 1) / 2) . $3 . $4 * 2|ge' ifile.txt > ofile.txt
[EDIT]
If your ifile.txt looks like:
this is a text file
assign x to 9 and y to 10.0702 45
define f(x)=x+y
- There are more than one space before the numbers.
- One more value exists at the end (after whitespaces).
Then please try the following instead:
pat="(assign x to +)([[:digit:]]+)( and y to +)([[:digit:].]+)( +)([[:digit:].]+)"
while IFS= read -r line; do
if [[ $line =~ $pat ]]; then
echo "#$line"
x2=$(echo "(${BASH_REMATCH[2]} + 1) / 2" | bc)
y2=$(echo "${BASH_REMATCH[4]} * 2" | bc)
y3=$(echo "${BASH_REMATCH[6]} * 2" | bc)
echo "${BASH_REMATCH[1]}$x2${BASH_REMATCH[3]}$y2${BASH_REMATCH[5]}$y3"
else
echo "$line"
fi
done < ifile.txt > ofile.txt
Result:
this is a text file
#assign x to 9 and y to 10.0702 45
assign x to 5 and y to 20.1404 90
define f(x)=x+y
The plus sign after a whitespace is a regex quantifier
and defines the number of repetition. In this case it matches one or more whitespace(s).