私はリアルタイムでWi-Fi使用量を読み込み、$HOME
システムが接続されているすべてのWi-Fiについて「usage」というファイル(ディレクトリ)に書き込むbashスクリプトを作成しました。スクリプトはうまくいきますが、2つの問題が私を悩ませています。
「ディスク全体の書き込み」が増加し続けます。
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を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
このスクリプトにはさらに問題がある可能性があるため、同じ競合条件分析を適用し、スクリプトの残りの部分に正しい引用を適用することをお勧めします。