Когда скрипт вырастает, то понять, где он тратит время, становится сложно. Можно, конечно, запускать
time ./script.sh, но это покажет лишь общую длительность.А если внутри десятки функций, то какая из них проседает? Решается просто: встроенным профилированием на чистом bash.
▪️ Идея
Оборачиваем каждую функцию в таймер, считаем суммарное время вызовов и количество запусков.
В конце - печатаем красивые итоги.
🛠 Минимальный пример
#!/usr/bin/env bash
declare -A FN_CALLS FN_TIME
# Обертка для функции
trace() {
local fn=$1
eval "
$fn() {
local start=\$(date +%s.%N)
__${fn}_impl \"\$@\"
local end=\$(date +%s.%N)
local delta=\$(awk -v s=\$start -v e=\$end 'BEGIN{print e-s}')
((FN_CALLS[$fn]++))
FN_TIME[$fn]=\$(awk -v a=\${FN_TIME[$fn]:-0} -v d=\$delta 'BEGIN{print a+d}')
}
"
}
# Пример функций
__work_impl() { sleep 0.2; }
__fast_impl() { echo ok >/dev/null; }
trace work
trace fast
# Нагрузочный сценарий
for i in {1..5}; do
work
fast
done
# Вывод статистики
echo -e "\nFunction Stats:"
for fn in "${!FN_CALLS[@]}"; do
avg=$(awk -v t=${FN_TIME[$fn]} -v c=${FN_CALLS[$fn]} 'BEGIN{printf "%.4f", t/c}')
printf "%-10s calls=%-3s total=%.4fs avg=%.4fs\n" "$fn" "${FN_CALLS[$fn]}" "${FN_TIME[$fn]}" "$avg"
done
▪️ Пример вывода:
Function Stats:
work calls=5 total=1.0012s avg=0.2002s
fast calls=5 total=0.0003s avg=0.0001s
▪️ Как это работает
Каждая реальная функция хранится как __name_impl.
Обернутый вариант считает время (date +%s.%N) до и после вызова.
Сохраняет статистику в ассоциативных массивах.
BashTex 📱 #bash