Ubuntu Server 16.04(xenial)を使用しており、指定された数のシステムが起動してから一度コマンドを実行してから自動的に削除したいと思います。解決策は2段階で構成されているようです。
にコマンドを追加します
/etc/bash.bashrc
。x回実行した後削除されるように
bash.bashrc
、何とか。
bash.bashrc
最後に、次のコマンドを追加してソリューションの最初のステップを完了しました。
echo "Welcome!"
Bashでこれを行う方法はありますか?
更新 - これは私が試したが失敗したものです。
cat <<-"EOF" > /opt/bootmsg.sh
#!/bin/bash
echo welcome!
count='0'
((count++))
sed -i -e "s/^count='[01234]'$/count='${count}'/" "$0"
if ((count>=5)); then rm -- "$0"; fi
EOF
chmod +x /opt/bootmsg.sh
# Add in the end of bash.bashrc:
# [ -f /opt/bootmsg.sh ] && /opt/bootmsg.sh
コードで間違った点が見つかった場合は、修正されたコードバージョンで答えを投稿し、私が間違っていることを説明してください。
ベストアンサー1
一般化する
主な問題は、スクリプトが実行された回数、つまり連続したスクリプト実行の間にどのように持続するかを追跡する方法を見つけることです。スクリプトが終了した後、環境変数はその値を保持しないため、環境変数を使用してこれを行うことはできません。
最も明確な方法は、この数字をファイルに保存することです。スクリプトはファイルから値を読み取った後、更新された値をファイルに書き戻すことができます。この数の情報を保存するために2番目のファイルを使用したくない場合は、スクリプトを独自に更新することができます(例sed
:両方のアプローチを説明するためのサンプルソリューションを提供しました)。
ソリューションは環境変数を更新し、それを使用して状態を追跡しようとしますが、スクリプトの実行間に環境変数が保持されないため、ソリューションは失敗します。
婦人声明:私は明示的に要求されたので、単一のファイルソリューションの例を示しましたが、個人的には自己修正スクリプトを含まないソリューションを好みます。通常、自己修正スクリプトは安定性が低く、デバッグが困難で理解しにくい傾向があります。
解決策1:ファイルを使用して数を保存する
以下は、必要な再起動回数を追跡するためにファイルを使用するソリューションです。
#!/usr/bin/env bash
# /opt/welcome.sh
# Read number of remaining reboots from file
REBOOTS="$(head -1 /var/reboots)"
# If the number of reboots is positive then
# execute the command and decrement the count
if [[ "${REBOOTS}" -ge 0 ]]; then
# Run some commands
echo "Doing some stuff... ($(( ${REBOOTS} - 1 )) left)"
fi
# Decrement the reboot count
REBOOTS="$(( ${REBOOTS} - 1 ))"
# Update the file
echo "${REBOOTS}" > /var/reboots
# If we've run out of reboots then delete the files
if [[ ! "${REBOOTS}" -gt 0 ]]; then
rm /var/reboots
rm -- "$0"
fi
以下は、この特定のスクリプトの実行例です。
user@host:~$ echo 3 > /var/reboots
user@host:~$ bash /opt/welcome.sh
Doing some stuff... (2 left)
user@host:~$ bash /opt/welcome.sh
Doing some stuff... (1 left)
user@host:~$ bash /opt/welcome.sh
Doing some stuff... (0 left)
user@host:~$ bash /opt/welcome.sh
bash: /opt/welcome.sh: No such file or directory
解決策2:自己修正スクリプトの使用
あるいは、count変数をスクリプト自体に含めてそれに更新することもできますsed
。たとえば、次のようになります。
#!/usr/bin/env bash
# /opt/welcome.sh
# Read number of remaining reboots from file
declare REBOOTS=3
# If the number of reboots is positive then
# execute the command and decrement the count
if [[ "${REBOOTS}" -ge 0 ]]; then
# Run some commands
echo "Doing some stuff... ($(( ${REBOOTS} - 1 )) left)"
fi
# Decrement the reboot count
REBOOTS="$(( ${REBOOTS} - 1 ))"
# Update the script
sed -i -e "s/^declare REBOOTS.*\$/declare REBOOTS=${REBOOTS}/" "$0"
# If we've run out of reboots then delete the script
if [[ ! "${REBOOTS}" -gt 0 ]]; then
rm -- "$0"
fi
添付ファイルがなくても同じ効果が表示されます。
失敗したソリューション試行分析
修正する:質問に次の解決策を追加しました。
cat <<-"WELCOME" > /opt/welcome.sh
#!/bin/bash
echo='welcome'
count='0'
((count+1))
if ((count>=5)) then rm -- "$0" fi
WELCOME
chmod +x /opt/welcome.sh
# Add in the end of bash.bashrc:
# [ -f /opt/welcome.sh ] && /opt/welcome.sh
なぜこのソリューションが機能しないのかを尋ねます。実行する実際のスクリプトは次のとおりです。
#!/bin/bash
echo='welcome'
count='0'
((count+1))
if ((count>=5)) then rm -- "$0" fi
((count>=))
rm -- "$0", i.e. you probably intended for you
上記のコードを実行しようとしたときに直面した最初の(明らかな)問題は、次のように条件式の後とifステートメントの本文の後にセミコロンがないことです。
if ((count>=5)); then rm -- "$0"; fi
これらの変更を行った後、スクリプトは実行されますが、何の効果もありません。理由を理解するために、各行を順番に見てみましょう。
echo='welcome'
この行は
echo
文字列を格納する変数を生成しますwelcome
。このコマンドは出力を生成しません。文字列を印刷するには、welcome
次のものを使用する必要があります。echo
注文する、環境変数ではありません名前付き例えば「エコ」echo welcome
。count='0'
この行は
count
値を格納する変数を生成します0
。これはcount
次のことを意味します。0
すべてスクリプトの繰り返し。((count+1))
この行は、変数に関連する算術式を評価します
count
。これはまったく影響しません。欲しいなら増加count変数を使用すると、同様の操作を実行できます((count++))
。また、値を正しく増やしてもスクリプトがcount
終了しても、この変更は保持されません。しかもした変更を維持するには、前の行()を上書きしますcount=0
。if ((count>=5)); then rm -- "$0"; fi
count
この行は、変数が.以上の場合はスクリプトファイルを削除します5
。しかし、count
Equalsしかないので、0
これは起こりません。
count
解決策を試すときに発生する基本的な問題は、スクリプトの実行間で値を保持する方法に関する問題を解決できないことです。count
各実行時にリセットします。0
スクリプトの反復間で値を保持する最も明白な方法は、ファイルから値を読み取り、更新された値をそのファイルに書き換えることです。したがって、最初のソリューションです。
自分を単一のファイルに制限するには、そのファイルの特別な行(プログラミング方法で識別できるように区別しやすい行)に値を格納して、本質的に同じことを実行してからスクリプトを作成できます。各行の値を更新するために、反復後にそれ自体を変更します。したがって、2番目の解決策です。
最小限の修正と修正で解決しようとする試み
特定のソリューションがスタンドアロン(自己修正)ファイルとして機能するように追加しようとしたため、以下は、動作に必要な変更を最小限に抑えるスクリプトの修正バージョンです。
#!/bin/bash
echo welcome
count='0'
((count++))
sed -i -e "s/^count='[01234]'$/count='${count}'/" "$0"
if ((count>=5)); then rm -- "$0"; fi
/opt/welcome.sh
投稿のように保存すると、次のようにテストできます。
user@host:~$ bash /opt/welcome.sh
welcome
user@host:~$ bash /opt/welcome.sh
welcome
user@host:~$ bash /opt/welcome.sh
welcome
user@host:~$ bash /opt/welcome.sh
welcome
user@host:~$ bash /opt/welcome.sh
welcome
user@host:~$ bash /opt/welcome.sh
bash: /opt/welcome.sh: No such file or directory
追加コメント
.bashrc
また、再起動時にスクリプトを実行したいが、新しいシェルセッションを開くたびに実行できるファイルからスクリプトを呼び出すと言います。起動時にスクリプトを実行する方法はいくつかあります。そのほとんどは特定のオペレーティングシステムによって異なります。
詳細については、次の記事を確認してください。
最後の解決策
コメントで深い議論をした後、実際に欲しいのは、タスクリストの変更に関する通知を表示するスクリプトであることが明らかになりました。
再起動後に初めてログインしたときにタスクが表示されます。また、5回の再起動後にジョブが消えることを望みます。
私たちは代替ソリューションを考えました。新しいソリューションは、複数のユーザーが同時に作業できるマルチユーザーソリューションです。 2つのシステム全体のスクリプトと2つのユーザー固有のデータファイルを使用します。
~/.tasks
count:description
コロンで区切られたペアのリスト(各ジョブごとに1つ)を格納するユーザー固有のファイル。~/.reminder-flag
最後の実行以降にジョブ通知が表示されたかどうかを追跡するユーザー固有のステータスファイル。/usr/local/bin/update-task-counts.sh
.tasks
すべての数を減らし、数が0のジョブを削除してファイルを更新するシェルスクリプト。reminder-flag
/usr/local/bin/print-tasks.shファイルを確認して更新し、すべてのジョブの説明を印刷するシェルスクリプト。
以下はサンプル~/.tasks
ファイルです。
5:This is task one.
3:This is task two.
1:Yet another task.
このリストの最初のジョブは合計5回表示され、2番目のジョブは合計3回表示され、最後のジョブは1回のみ表示されます。
また、このファイルを読み取って更新するにはスクリプトが必要です。以下はこれを実行できるスクリプトです。
#!/usr/bin/env bash
# /usr/local/bin/update-task-counts.sh
# Set the location of the task file
TASKFILE="${HOME}/.tasks"
# Set the location of the reminder flag file
REMINDFILE="${HOME}/.remind-tasks"
# Set a flag so that we know we need to print the task messages
echo 1 > "${REMINDFILE}"
# If there is no task file, then exit
if [[ ! -f "${TASKFILE}" ]]; then
exit 0
fi
# Create a temporary file
TEMPFILE="$(mktemp)"
# Loop through the lines of the current tasks-file
while read line; do
# Extract the description and the remaining count for each task
COUNT="${line/:*/}"
DESC="${line/*:/}"
# Decrement the count
((COUNT--))
# If the count is non-negative then add it to the temporary file
if [[ "${COUNT}" -ge 0 ]]; then
echo "${COUNT}:${DESC}" >> "${TEMPFILE}"
fi
done < "${TASKFILE}"
# Update the tasks file (by replacing it with the temporary file)
mv "${TEMPFILE}" "${TASKFILE}"
このスクリプトを実行すると、ジョブファイルの各行を繰り返し、各ジョブの数を減らし、正の数のジョブのみを含むようにジョブファイルを更新します。
次に、ジョブリストのジョブを印刷するスクリプトが必要です。
#!/usr/bin/env bash
# /usr/local/bin/print-tasks.sh
# Set the location of the task file
TASKFILE="${HOME}/.tasks"
# Set the location of the reminder flag file
REMINDFILE="${HOME}/.remind-tasks"
# If there is no task file, then exit
if [[ ! -f "${TASKFILE}" ]]; then
exit 0
fi
# If the reminder flag isn't set, then exit
FLAG="$(head -1 ${REMINDFILE})"
if [[ ! "${FLAG}" -eq 1 ]]; then
exit
fi
# Loop through the lines of the current tasks-file
while read line; do
# Extract the description for each task
DESC="${line/*:/}"
# Display the task description
echo "${DESC}"
done < "${TASKFILE}"
# Update the flag file so we know not to display the task list multiple times
echo 0 > "${REMINDFILE}"
最後にすべきことは、両方のスクリプトが適切なタイミングで呼び出されることを確認することです。再起動時にスクリプトを実行するには、update-task-counts.sh
ユーザーのcrontabからそれを呼び出すことができます。つまり、crontabに次の行を追加します(例:を使用してcrontab -e
)。
@reboot /bin/bash /usr/local/bin/update-task-counts.sh
このcronテクノロジの詳細については、次の記事を参照してください。
ユーザーが最初のシェルセッションに入ったときにスクリプトが実行されるようにするには、次の行をprint-tasks.sh
に追加してユーザーのbashプロファイルからスクリプトを呼び出すことができます~/.bash_profile
。
bash /usr/local/bin/print-tasks.sh
それでは、サンプルファイルを使って次のスクリプトを実行してみましょう~/.tasks
。
5:This is task one.
3:This is task two.
1:Yet another task.
実行せずに通知を有効にする方法は次のとおりですupdate-task-counts.sh
。
user@host:~$ echo 1 > ~/.reminder-flag
print-task.sh
スクリプトを手動でテストするには、スクリプトを2回実行するだけです。
user@host:~$ bash /usr/local/bin/print-tasks.sh
This is task one.
This is task two.
Yet another task.
user@host:~$ bash /usr/local/bin/print-tasks.sh
user@host:~$
最初の呼び出しでのみ印刷されます。print-task.sh
対話を手動でテストするには、次のupdate-task-counts.sh
ように一緒に実行します。
このファイルを使用して上記のスクリプトを手動で実行すると、結果は次のようになります。
user@host:~$ bash update-task-counts.sh
user@host:~$ bash print-tasks.sh
This is task one.
This is task two.
Yet another task.
user@host:~$ bash update-task-counts.sh
user@host:~$ bash print-tasks.sh
This is task one.
This is task two.
user@host:~$ bash update-task-counts.sh
user@host:~$ bash print-tasks.sh
This is task one.
This is task two.
user@host:~$ bash update-task-counts.sh
user@host:~$ bash print-tasks.sh
This is task one.
user@host:~$ bash update-task-counts.sh
user@host:~$ bash print-tasks.sh
This is task one.
user@host:~$ bash update-task-counts.sh
user@host:~$ bash print-tasks.sh
user@host:~$
これは可能です。