나는 실시간으로 Wi-Fi 사용량을 읽고 $HOME
시스템이 연결된 모든 Wi-Fi에 대해 "usage"라는 파일(디렉토리)에 기록하는 bash 스크립트를 작성했습니다. 스크립트는 잘 작동하지만 두 가지 문제가 나를 괴롭힙니다.
"총 디스크 쓰기"가 계속 증가합니다.
usage
내 시스템이 Wi-Fi에 연결될 때마다 내 스크립트가 while 루프에서 파일을 계속 업데이트하기 때문에 이것이 정상/예상입니까? 사용되는 총 메모리는 496KB로 고정되어 있습니다.스크립트가 실행되는 동안 많은 임시 파일이 계속 생성되며 이 파일도 즉시 삭제됩니다. 아래 이미지에서 로 시작하는 파일이
sed*
임시 파일이라는 점에 유의하세요. 디렉터리를 새로 고치면 파일이 사라지고 새 파일이 계속 나타납니다.
내 스크립트에 문제가 있나요? 아니면 이런 행동이 정상인가요?
스크립트:
#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
filepath="$HOME/usage" #since cron jobs have a current working directory usually set as home
date_check() {
if [ -f $filepath ]
then
cur_date=$(date +%s)
last_mod_date=$(date -r $filepath +%s)
if [ $(date +%m) == 02 ] && [ $cycle_date -gt 28 ]
then
cycle_date=28
fi
cyc_date=$(date -d "$(date +%Y)-$(date +%m)-$cycle_date" +%s)
if [[ $cyc_date -gt $last_mod_date ]] && [[ $cyc_date -lt $cur_date ]]
then
rm $filepath
fi
fi
}
wifi_record_update() {
availability=""
for dev in $net_interface
do
if [ $(iwconfig wlp0s20f3 | grep ESSID | cut -d: -f2) != "off/any" ]
then
availability="yes"
ssid=$(iwconfig wlp0s20f3 | grep ESSID | cut -d: -f2)
mac=$(iwconfig wlp0s20f3 | grep "Access Point" | tr -s ' ' | cut -d ' ' -f7)
if grep -Fq "$mac" $filepath
then
used=$(grep "$mac" $filepath | cut -d ' ' -f3)
else
echo "$mac $ssid 0" >> $filepath
fi
break
fi
done
sed -i "/off\/any/d" $filepath ###to delete garbage records which sometimes get collected with mac name set as off/any
}
########### main
##### identifying all wifi network adapter interfaces
net_interface=()
for dev in $(ls /sys/class/net/);
do
if [ -d "/sys/class/net/$dev/wireless/" ]
then
net_interface+=("$dev")
fi
done
##### getting cycle date
if [ -f $filepath ]
then
cycle_date=$(head -n 1 $filepath)
else
cycle_date=1 #default date at the start of first ever run of this script
fi
##### deletes $filepath file if the cycle date is passed
date_check;
if ! [[ -f $filepath ]]
then
echo $cycle_date > $filepath
fi
##### main while loop
while true
do
wifi_record_update
while [ "$availability" != "" ]
do
prev=$cur
cur=$(cat /sys/class/net/$dev/statistics/rx_bytes)
add=$((cur-prev))
echo "$(awk -v ad=$add -v ma=$mac '{if ($1==ma) {$3=$3+ad}; print $0 }' $filepath)" > $filepath ### updating the used value
wifi_record_update
done
done
exit 0
https://github.com/atul-g/bash_utility_scripts/blob/master/wifi_usage/wifi_usage.sh
답변1
예, 쉘 스크립트에 문제가 있습니다. 음, 몇 가지가 있습니다. 관련 기능에 대한 코드를 빠르게 검토해 보겠습니다.
wifi_record_update() {
availability=""
for dev in $net_interface
do
if [ $(iwconfig wlp0s20f3 | grep ESSID | cut -d: -f2) != "off/any" ]
then
availability="yes"
ssid=$(iwconfig wlp0s20f3 | grep ESSID | cut -d: -f2)
mac=$(iwconfig wlp0s20f3 | grep "Access Point" | tr -s ' ' | cut -d ' ' -f7)
if grep -Fq "$mac" $filepath
then
used=$(grep "$mac" $filepath | cut -d ' ' -f3)
else
echo "$mac $ssid 0" >> $filepath
fi
break
fi
done
sed -i "/off\/any/d" $filepath ###to delete garbage records which sometimes get collected with mac name set as off/any
}
나중에 쉽게 참조할 수 있도록 다음 검토가 적용됩니다.스크립트 제출 c65636db940235fd7458fb4c4432324401400658.
자주 호출하기 sed -i
때문에 많은 임시 파일이 생성됩니다(나중에 삭제되지만).
또한 sed -i
처음에 파일에 기록되어서는 안 되는 기록을 삭제하는 데에만 이 기능을 사용하고 있습니다.
게다가 다음 줄이 작성되지 않도록 설계된 코드가 이미 마련되어 있습니다.
if [ $(iwconfig wlp0s20f3 | grep ESSID | cut -d: -f2) != "off/any" ]
then
...
fi
sed -i
그 추악한 해킹을 추가하는 대신 먼저 위 메커니즘이 작동하지 않는 이유를 조사해야 합니다.
이제 스크립트의 실제 문제를 살펴보겠습니다. 일반적인 경쟁 조건을 설정했습니다.
if [ $(iwconfig wlp0s20f3 | grep ESSID | cut -d: -f2) != "off/any" ]
then
...
ssid=$(iwconfig wlp0s20f3 | grep ESSID | cut -d: -f2)
...
fi
SSID를 두 번 요청했는데 두 줄 사이에서 SSID가 변경되었을 수 있습니다. 그렇기 때문에 추가 확인에도 불구하고 "꺼짐/모든" 줄이 표시됩니다. 올바른 해결 방법은 SSID를 한 번만 가져오고 해당 값만 처리하는 것입니다.
ssid=$(iwconfig wlp0s20f3 | grep ESSID | cut -d: -f2)
if [ "$ssid" != off/any ]
then
...
fi
아, 그런데 !=
비교의 잘못된 부분을 인용하셨네요. 무해한 문자열 "off/any"를 인용할 필요는 없지만 하위 쉘 호출의 결과를 인용하는 데에는 충분한 이유가 있습니다 "$(...)"
. 그리고 변경 후 볼 수 있듯이 각 변수가 확장된다는 점을 인용해 보세요.
이 오류 및 이와 유사한 일반적인 인용 오류를 방지하려면 우수한 스크립트를 통해 스크립트를 실행하는 것이 좋습니다.주택 검사도구. 프로덕션에서 사용하기 전(그리고 우리가 다른 작업을 수행하기 전에) 작성하는 모든 쉘 스크립트에 대해 다음을 수행하십시오.
shellcheck YOUR-SCRIPT.sh
하지만 그 이상이 있습니다! 이러한 수정 후에도 ssid/mac
때때로 잘못된 조합을 생성 하는 두 번째 경쟁 조건이 여전히 존재합니다 . 그 이유는 정확히 동일합니다. iwconfig
서로 다른 시점에 두 번 결과를 요청하고 있으며 그 동안 액세스 포인트가 변경되었을 수 있습니다.
ssid=$(iwconfig wlp0s20f3 | grep ESSID | cut -d: -f2)
if [ "$ssid" != off/any ]
then
...
mac=$(iwconfig wlp0s20f3 | grep "Access Point" | tr -s ' ' | cut -d ' ' -f7)
...
fi
여기서 올바른 해결책은 전체 iwconfig
출력을 한 번만 가져오고 정확히 동일한 시점에서 SSID와 MAC를 추출하는 것입니다. 의 출력이 iwconfig
상대적으로 작기 때문에 파일을 만드는 대신 변수를 사용합니다. 또한 쉘 변수에는 여러 줄이 포함될 수 있으며 echo
올바르게 인용하면 올바르게 재현됩니다 (예: echo "$iwconfig_output"
다음 대신:) .echo $iwconfig_output
iwconfig_output=$(iwconfig wlp0s20f3)
ssid=$(echo "$iwconfig_output" | grep ESSID | cut -d: -f2)
if [ "$ssid" != off/any ]
then
...
mac=$(echo "$iwconfig_output" | grep "Access Point" | tr -s ' ' | cut -d ' ' -f7)
...
fi
이 스크립트에는 더 많은 문제가 있을 수 있으므로 동일한 경쟁 조건 분석을 적용하고 스크립트의 나머지 부분에 올바른 인용을 적용하는 것이 좋습니다.