ディスク全体の書き込みを増やすためのBashスクリプト

ディスク全体の書き込みを増やすためのBashスクリプト

私はリアルタイムでWi-Fi使用量を読み込み、$HOMEシステムが接続されているすべてのWi-Fiについて「usage」というファイル(ディレクトリ)に書き込むbashスクリプトを作成しました。スクリプトはうまくいきますが、2つの問題が私を悩ませています。

  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を2回要求しましたが、2行の間でSSIDが変更された可能性があります。そのため、追加の確認にもかかわらず、「オフ/すべて」の行が表示されます。正しい回避策は、SSIDを一度だけ取得し、その値のみを処理することです。

ssid=$(iwconfig wlp0s20f3 | grep ESSID | cut -d: -f2)
if [ "$ssid" != off/any ]
then
    ...
fi

ああ、ところで!=比較の間違った部分を引用しましたね。無害な文字列 "off / any"を引用する必要はありませんが、サブシェル呼び出しの結果を引用するのに十分な理由があります"$(...)"。そして、変更後に見られるように、各変数が拡張されることを引用してみてください。

このエラーや同様の一般的な引用エラーを回避するには、優れたスクリプトでスクリプトを実行することをお勧めします。住宅検査ツール。本番環境で使用する前(および他の作業を実行する前に)作成するすべてのシェルスクリプトに対して、次の手順を実行します。

shellcheck YOUR-SCRIPT.sh

しかし、それ以上があります!これらの修正後も、ssid/mac時々誤った組み合わせを生成する2番目の競合条件がまだ存在します。その理由はまったく同じです。iwconfig異なる時点で2回の結果を要求しており、その間にアクセスポイントが変更された可能性があります。

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

このスクリプトにはさらに問題がある可能性があるため、同じ競合条件分析を適用し、スクリプトの残りの部分に正しい引用を適用することをお勧めします。

おすすめ記事