총 디스크 쓰기를 늘리기 위한 Bash 스크립트

총 디스크 쓰기를 늘리기 위한 Bash 스크립트

나는 실시간으로 Wi-Fi 사용량을 읽고 $HOME시스템이 연결된 모든 Wi-Fi에 대해 "usage"라는 파일(디렉토리)에 기록하는 bash 스크립트를 작성했습니다. 스크립트는 잘 작동하지만 두 가지 문제가 나를 괴롭힙니다.

  1. "총 디스크 쓰기"가 계속 증가합니다. usage내 시스템이 Wi-Fi에 연결될 때마다 내 스크립트가 while 루프에서 파일을 계속 업데이트하기 때문에 이것이 정상/예상입니까? 사용되는 총 메모리는 496KB로 고정되어 있습니다. 여기에 이미지 설명을 입력하세요.

  2. 스크립트가 실행되는 동안 많은 임시 파일이 계속 생성되며 이 파일도 즉시 삭제됩니다. 아래 이미지에서 로 시작하는 파일이 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

이 스크립트에는 더 많은 문제가 있을 수 있으므로 동일한 경쟁 조건 분석을 적용하고 스크립트의 나머지 부분에 올바른 인용을 적용하는 것이 좋습니다.

관련 정보