#awk
#awk
Вопрос:
Мне нужна помощь! У меня есть несколько точек ABCDEF … с такими позициями:
A 0.00 0.50 0.10
B 1.00 2.50 2.00
C 0.70 0.88 1.29
D 2.13 2.90 0.11
E 1.99 0.77 0.69
...
Я стремлюсь вычислить расстояние AB, BC, CD, EF, … и их сумму с выводом, который имеет вид, подобный этому:
sum_distance(AB)
sum_distance(AB BC)
sum_distance(AB BC CD)
sum_distance(AB BC CD DE)
sum_distance(AB BC CD DE EF)
....
Я нашел в Интернете awk, который может это сделать и применить к моему случаю. Однако результат или ошибка не были экспортированы на экран. Не могли бы вы, пожалуйста, помочь мне с этой ситуацией?
оболочка bash, awk
awk 'FNR==NR { a[NR]=$0; next } { for (i=FNR 1;i<=NR-1;i ) {split(a[i],b); print $1 "-" b[1], sqrt(($2-b[2])^2 ($3-b[3])^2 ($4-b[4])^2) | "column -t" } NR--}'
Вывод:
2.934280150
4.728297987
7.470140434
9.682130488
11.92469598
......
Ответ №1:
Вам не нужен такой сложный скрипт для этой тривиальной задачи. Попробуйте это вместо:
awk 'NR>1{ printf "%.9fn",s =sqrt(($2-x)^2 ($3-y)^2 ($4-z)^2) }
{ x=$2;y=$3;z=$4 }' file
Для всех точек, кроме A
, вычислите расстояние, добавьте его к сумме s
и выведите сумму. Для всех точек сохраните координаты в x, y, z
для следующего вычисления. Его вывод выглядит следующим образом с помощью gawk:
2.934280150
4.728297987
7.470140434
9.682130488
Ответ №2:
Каково основное правило? (никогда не используйте код из Интернета, который вы не понимаете …)
Проблема со awk
скриптом, который вы пытаетесь использовать, заключается в том, что это не совсем ваш случай. Путем установки FNR==NR
, а затем использования ограничений цикла (i=FNR 1;i<=NR-1;i )
ожидается, что будет несколько входных файлов. В вашем случае вы действительно можете упростить сценарий, полностью удалив цикл, поскольку у вас есть только один входной файл.
Вам нужно только сохранить первую строку, затем с помощью next
чтения следующей строки вычислить и вывести расстояние между предыдущей строкой и текущей, установить текущую строку в качестве строки в a[]
массиве и повторять, пока у вас не закончатся строки, например
awk '{
a[NR]=$0
if (NR == 1)
next
split(a[NR-1],b)
printf "%st%sn", b[1] "-" $1,
sqrt(($2-b[2])^2 ($3-b[3])^2 ($4-b[4])^2)
a[NR]=$0
}'
Пример входного файла
$ cat f
A 0.00 0.50 0.10
B 1.00 2.50 2.00
C 0.70 0.88 1.29
D 2.13 2.90 0.11
E 1.99 0.77 0.69
Пример использования / вывода
Просто вставьте скрипт в терминал, добавив имя файла в конце, например
$ awk '{
> a[NR]=$0
> if (NR == 1)
> next
> split(a[NR-1],b)
> printf "%st%sn", b[1] "-" $1,
> sqrt(($2-b[2])^2 ($3-b[3])^2 ($4-b[4])^2)
> a[NR]=$0
> }' f
A-B 2.93428
B-C 1.79402
C-D 2.74184
D-E 2.21199
Просмотрите все и дайте мне знать, если у вас возникнут дополнительные вопросы.
Ответ №3:
Попробуйте это:
awk 'function d(a,b){split(a,x);split(b,y);return sqrt((x[2]-y[2])^2 (x[3]-y[3])^2 (x[4]-y[4])^2);} {p[FNR]=$0} FNR>1{sum[FNR]=sum[FNR-1] d(p[FNR-1],p[FNR]);printf "%.9fn",sum[FNR];}' file
С file
таким контентом , как этот:
A 0.00 0.50 0.10
B 1.00 2.50 2.00
C 0.70 0.88 1.29
D 2.13 2.90 0.11
E 1.99 0.77 0.69
обеспечит вывод, подобный этому:
2.934280150
4.728297987
7.470140434
9.682130488
Вы не указали точку F
, поэтому ваша последняя строка вывода на самом деле не может быть учтена здесь.
Поместить в несколько строк здесь:
awk '
function d(a,b){
split(a,x);
split(b,y);
return sqrt((x[2]-y[2])^2 (x[3]-y[3])^2 (x[4]-y[4])^2);
}
{p[FNR]=$0}
FNR>1{
sum[FNR]=sum[FNR-1] d(p[FNR-1],p[FNR]);
printf "%.9fn",sum[FNR];
}' file
Здесь все довольно просто, функция d
для расстояния. И повторно использовать сумму предыдущей строки.
И для развлечения, если вы хотите вычислить общее расстояние графика, изначально с одной точкой и постепенно добавлять точки на график. Т.Е. :
sum_distance(AB)
sum_distance(AB BC AC)
sum_distance(AB BC AC AD BD CD)
...
Тогда будет достаточно небольшого улучшения, например:
$ awk 'function d(a,b){split(a,x);split(b,y);return sqrt((x[2]-y[2])^2 (x[3]-y[3])^2 (x[4]-y[4])^2);} {p[FNR]=$0} FNR>1{sum[FNR]=sum[FNR-1];for(i=FNR-1;i>0;i--)sum[FNR] =d(p[i],p[FNR]);printf "%.9fn",sum[FNR];}' file
2.934280150
6.160254691
14.349070561
22.466306583