Bashスクリプトの一部を終了するには2つの方法が必要です。
カウンターが事前定義された数値に達するか、ユーザーが手動でスクリプトがカウンターの現在の値を使用し続けるように強制します。
具体的には - USBドライブをリストしました。 15個があると、それを計算する関数が終了し、スクリプトが続行される可能性があります。
私のコードは次のとおりです。
scannew(){
NEW=0
OLD=$NEW
while [ true ]; do
# count the new drives
lsblk -r > drives.new
diff drives.old drives.new | grep disk | cut -d' ' -f 2 | sort > drives.all
NEW=$(wc -l drives.all | cut -d' ' -f1)
echo -en " Detected drives: $NEW \r"
sleep 0.01
if [ "$NEW" -eq "15" ]; then # exit if we reach the limit
break
fi
done
}
# SOME CODE...
lsblk -r > drives.old
scannew & # start live device counter in the background
SCAN_PID=$! # remember it's PID
wait $SCAN_PID 2>/dev/null # wait until it dies
echo "It's on!"
# REST OF THE CODE...
そのコマンドでさまざまな操作を試しましたが、結果read
的にスクリプトは常に読み取りが終了するのを待ち(ENTERキーを押した後)、「15制限」条件を無視することはできません。
たとえば、代わりに関数で試しましたread -t
。sleep
scannew()
scannew(){
NEW=0
OLD=$NEW
while [ true ]; do
# count the new drives
lsblk -r > drives.new
diff drives.old drives.new | grep disk | cut -d' ' -f 2 | sort > drives.all
NEW=$(wc -l drives.all | cut -d' ' -f1)
echo -en " Detected drives: $NEW \r"
read -t 0.01 -n 1 && break # read instead of sleep
if [ "$NEW" -eq "15" ]; then
break
fi
done
}
ただし、関数サブプロセスは標準入力にアクセスできないようで、read -t 0.01 -n 1 < /dev/stdin && break
これを使用すると機能しません。
どうすればいいですか?
ベストアンサー1
私が最初に言いたいことはあなたができるスクリプトの他の場所から再スキャンする予定がない場合は、とにかく行うので、含まれているすべてのscannew
項目をインライン化してください。wait
これは実際に通話がwc
長すぎる可能性があるという懸念であり、その場合は通話を終了できます。以下は、trap
プロセスに送信された信号をキャプチャし、それに独自のハンドラを設定できる簡単な設定です。
#! /usr/bin/env bash
# print a line just before we run our subshell, so we know when that happens
printf "Lets do something foolish...\n"
# trap SIGINT since it will be sent to the entire process group and we only
# want the subshell killed
trap "" SIGINT
# run something that takes ages to complete
BAD_IDEA=$( trap "exit 1" SIGINT; ls -laR / )
# remove the trap because we might want to actually terminate the script
# after this point
trap - SIGINT
# if the script gets here, we know only `ls` got killed
printf "Got here! Only 'ls' got killed.\n"
exit 0
ただし、作業を維持し、scannew
機能をバックグラウンドタスクとして実行するには、より多くのタスクを実行する必要があります。
ユーザー入力が必要なので、正しいアプローチはを使用することです。ただし、ユーザー入力を永遠に待たずにジョブが完了した後でも実行を続けるには、read
スクリプトが必要です。これを少しトリッキーにするのは、sが信号を処理できるようにする前に、現在のコマンドが完了するのを待つことです。私が知っている限り、スクリプト全体をリファクタリングしない唯一の解決策は、ループを挿入して使用することです。これはプロセスが完了するのに常に少なくとも1秒かかりますが、あなたのような場合はデフォルトでUSBデバイスを一覧表示するポーリングデーモンです。おそらく許可されます。scannew
read
bash
trap
read
while true
read -t 1
#! /usr/bin/env bash
function slow_background_work {
# condition can be anything of course
# for testing purposes, we're just checking if the variable has anything in it
while [[ -z $BAD_IDEA ]]
do
BAD_IDEA=$( ls -laR / 2>&1 | wc )
done
# `$$` normally gives us our own PID
# but in a subshell, it is inherited and thus
# gives the parent's PID
printf "\nI'm done!\n"
kill -s SIGUSR1 -- $$
return 0
}
# trap SIGUSR1, which we're expecting from the background job
# once it's done with the work we gave it
trap "break" SIGUSR1
slow_background_work &
while true
do
# rewinding the line with printf instead of the prompt string because
# read doesn't understand backslash escapes in the prompt string
printf "\r"
# must check return value instead of the variable
# because a return value of 0 always means there was
# input of _some_ sort, including <enter> and <space>
# otherwise, it's really tricky to test the empty variable
# since read apparently defines it even if it doesn't get input
read -st1 -n1 -p "prompt: " useless_variable && {
printf "Keypress! Quick, kill the background job w/ fire!\n"
# make sure we don't die as we kill our only child
trap "" SIGINT
kill -s SIGINT -- "$!"
trap - SIGINT
break
}
done
trap - SIGUSR1
printf "Welcome to the start of the rest of your script.\n"
exit 0
もちろん、本当に必要なものがデーモンであるか、USBデバイスの数の変化を監視することであれば、どちらsystemd
がよりエレガントなものを提供するかを検討する必要があります。