我正在尝试编写一个bash脚本,该脚本使用一个函数在后台生成多个应用程序,然后将它们的PID存储在以应用程序名称为键的关联数组中。当我使用for循环遍历具有应用程序路径的另一个数组时遇到问题。生成所有应用程序后,我将回显数组的所有值,但仅显示最后生成的应用程序的PID。这会在脚本的其余部分中引发一些问题。
我已经找到this thread可能会发生这种情况的原因,但是我无法针对自己的情况实施此修复程序。有人可以指出正确的方向吗?
proc_name=( application1 application2 application3 )
declare -A proc_pid
spawn_application () {
if [ $# -ne 1 ] || [ ! -x $1 ]
then
echo "$FUNCNAME: usage $FUNCNAME <proc_name>"
if [ $# -ne 1]
then
echo "$FUNCNAME: invalid amount of arguments, expected 1 received $#"
elif [ ! -x $1 ]
then
echo "$FUNCNAME: $1 is not an executable file"
fi
exit 1
else
./$1 $host_ipaddr &
proc_pid[$1]=$!
echo "$0: starting $1 with PID: ${proc_pid[$1]}"
fi
}
for proc in ${proc_name[@]}
do
spawn_application $proc
done
echo ${proc_pid[@]}
这是完整的脚本:
#!/bin/bash
#####################
# Functions #
#####################
# init - deletes all existing _metrics.csv files, populates array of executables, initializes map of proc_name:pid
init () {
rm ./*_metrics.csv >/dev/null 2>&1
proc_name=( application1 application2 application3 )
declare -A proc_pid
}
# spawn_application - takes a proc_name, checks that it is executable, and then runs the executable in the background
spawn_application () {
if [ $# -ne 1 ] || [ ! -x "$1" ]
then
echo "$FUNCNAME: usage $FUNCNAME <proc_name>"
if [ $# -ne 1 ]
then
echo "$FUNCNAME: invalid amount of arguments, expected 1 received $#"
elif [ ! -x "$1" ]
then
echo "$FUNCNAME: $1 is not an executable file"
fi
exit 1
else
./"$1" "$host_ipaddr" &
#proc_pid[$1]=$!
#echo "$0: starting $1 with PID: ${proc_pid[$1]}"
fi
}
# collect_proc_metrics - takes a proc_name and returns the elapsed time in seconds, cpu percentage, and memory percentage, passes these values to write_proc_metrics
collect_proc_metrics () {
if [ $# -ne 1 ]
then
echo "$FUNCNAME: usage $FUNCNAME <proc_name>"
if [ $# -ne 1 ]
then
echo "$FUNCNAME: invalid amount of arguments, expected 1 received $#"
fi
exit 1
else
read -r seconds cpu memory <<<"$(ps -q ${proc_pid["$1"]} -eo etimes=,%cpu=,%mem=)"
write_proc_metrics "$1" "$seconds" "$cpu" "$memory" &
fi
}
# collect_sys_metrics - takes no arguments, obtains the rx and tx rates, disk writes, and available disk space. Uses the last set elapsed time in seconds from the collect_proc_metrics. Automatically passes these values to write_sys_metrics
collect_sys_metrics () {
read -r rx tx <<<"$(ifstat en* | sed -n 4p | tr -s ' ' | awk '{print $7, $9}')"
disk_writes=$(iostat sda -k | tail -n 2 | tr -s ' ' | cut -d' ' -f4)
available_disk_space=$(df /dev/mapper/centos-root --output=avail --block-size=1M | tail -n 1)
write_sys_metrics "$seconds" "$rx" "$tx" "$disk_writes" "$available_disk_space" &
}
# write_proc_metrics - takes the elapsed time in seconds, cpu percentage, and memory percentage and appends them to a .csv file.
write_proc_metrics () {
if [ $# -ne 4 ]
then
echo "$FUNCNAME: usage $FUNCNAME <proc_name> <seconds> <cpu> <memory>"
echo "$FUNCNAME: invalid amount of arguments, expected 4 received $#"
else
local metricsout="$1_metrics.csv"
if [ ! -f "$metricsout" ]
then
echo "SECS,CPU%,MEM%" > "$metricsout"
fi
echo "$2,$3,$4" >> "$metricsout"
fi
}
# write_sys_metrics - takes the elapsed time in seconds, RX and TX rates, disk writes, and disk usage and appends them to a .csv file.
write_sys_metrics () {
if [ $# -ne 5 ]
then
echo "$FUNCNAME: usage $FUNCNAME <seconds> <rx> <tx> <disk_writes> <available_disk_space>"
echo "$FUNCNAME: invalid amount of arguments, expected 5 received $#"
else
local metricsout="system_metrics.csv"
if [ ! -f $metricsout ]
then
echo "SECS,RX,TX,kB_wrtn/s,MB_avail" > $metricsout
fi
echo "$1,$2,$3,$4,$5" >> system_metrics.csv
fi
}
# cleanup - loops through the proc_names in the proc_name array and kills their associated PID with a -9 flag.
cleanup () {
pkill -f application >/dev/null 2>&1
pkill -f ifstat >/dev/null 2>&1
#for proc in ${proc_name[@]}
#do
# kill -9 ${proc_pid[$proc]}
#done
}
trap cleanup EXIT
################
# Main #
################
if [ $# -ne 1 ]
then
echo "$0: usage $0 <host_ipaddr>"
echo "$0: invalid amount of arguments, expected 1 received $#"
else
init
host_ipaddr=$1
for proc in "${proc_name[@]}"
do
spawn_application "$proc"
proc_pid["$proc"]=$!
echo "$0: starting $proc with PID: ${proc_pid[$proc]}"
done
echo "${proc_pid[@]}"
# Starts the ifstat command as a background process.
ifstat -d1 -n
ifstat_pid=$( ps aux | grep "ifstat -d1" | head -1 | tr -s ' ' | cut -f2 -d' ' )
echo "$0: starting ifstat with PID: $ifstat_pid"
ifstat -nr
# Duration is how long to sample metrics in seconds. 900 seconds is 15 minutes
duration=$(( SECONDS+900 ))
# Interval is how long to wait between sampling
interval=5
while [ $SECONDS -lt $duration ]
do
for proc in "${proc_name[@]}"
do
collect_proc_metrics "$proc"
done
collect_sys_metrics
sleep $interval
done
cleanup
fi
输出:
[rje6459@localhost Desktop]$ ./apm.sh 129.21.229.64
./apm.sh: starting APM1 with PID: 48163
./apm.sh: starting APM2 with PID: 48164
./apm.sh: starting APM3 with PID: 48165
./apm.sh: starting APM4 with PID: 48166
./apm.sh: starting APM5 with PID: 48167
./apm.sh: starting APM6 with PID: 48168
48168
./apm.sh: starting ifstat with PID: 48170