[shell] btc daemonが起動していなかったら起動する

VPSでbtc daemonを運用としていると、何故か勝手に落ちることがあるので、バッチでnodeが動いているか確認して、動いていなかったら起動するコマンドをシェルスクリプトで実行する。

ログファイルに書き込む際に、command=”bitcoin-core.cli –getinfo –testnet | sudo tee -a ${log_FILE}”などとやってしまうと、retval=$?の返り値が、teeの方で判定されてしまうので、判定した後に書き込むとしないと行けない。

#!/bin/sh

today=$(date "+%Y%m%d")
time=$(date "+%Y%m%d %H:%M:%S")
log_FILE="./log/log_${today}.log"

if [ ! -e $log_FILE ]; then
    sudo touch ${log_FILE}
fi

command="bitcoin-core.cli --getinfo --testnet"
eval $command

retval=$?
if [ $retval -eq 0 ]
then
    echo "btc daemon is running. " $time | sudo tee -a ${log_FILE}
else
    echo "btc daemon is stopped. " $time | sudo tee -a ${log_FILE}
    # command="bitcoin-core.daemon -testnet -prune=1000"
    command="echo try again!"
    eval $command
fi

あとはこのシェルをcronで設定すれば良いだけですね。

### cronの設定
とりあえずバッチの実行時間は2時にしました。

$ dnf search crontabs
Failed to set locale, defaulting to C.UTF-8
AlmaLinux 8 – BaseOS 4.8 MB/s | 6.3 MB 00:01
AlmaLinux 8 – AppStream 7.1 MB/s | 12 MB 00:01
AlmaLinux 8 – Extras 36 kB/s | 20 kB 00:00
Extra Packages for Enterprise Linux 8 – x86_64 8.9 MB/s | 16 MB 00:01
nginx stable repo 26 kB/s | 55 kB 00:02
nginx mainline repo 77 kB/s | 135 kB 00:01
======================== Name Exactly Matched: crontabs ========================
crontabs.noarch : Root crontab files used to schedule the execution of programs

$ crontab -e

0 2 * * * /home/alma/command/btc_daemon.sh

$ systemctl restart crond
$ systemctl enable crond
$ systemctl status crond
● crond.service – Command Scheduler
Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor prese>
Active: active (running) since Sat 2024-02-24 21:38:44 JST; 43s ago
Main PID: 900566 (crond)
Tasks: 1 (limit: 5868)
Memory: 1.1M
CGroup: /system.slice/crond.service
└─900566 /usr/sbin/crond -n

あとは、明日ログを見て、実行されているようであれば、bitcoin-core.daemon のコメントアウトを外します。
※シェルを使うようになったのかと思うと感慨深い…

[shell] awkを使いたい

hoge.txt

1 2 3
4 5 6
awk '{print $1}' hoge.txt

$ bash cli.bash
1
4

awk '{print $0}' hoge.txt

$ bash cli.bash
1 2 3
4 5 6

awk '$1 ~ /^[0-9]/ { print $1; }' hoge.txt

$ bash cli.bash
1
4

sub(前, 後、入力文字列)

awk '$1 ~ /^[0-9]/ {sub("1", "10", $0); print $1; }' hoge.txt

$ bash cli.bash
10
4

置換、正規表現で検出しているのがわかります。

helpopts=$($bitcoin_cli -help 2>&1 | awk '$1 ~ /^-/ { sub(/=.*/, "="); print $1 }' )
//
commands=$(_bitcoin_rpc help 2>/dev/null | awk '$1 ~ /^[a-z]/ { print $1; }')

なるほど、ユーザのCLIの処理を実装するのはシェルスクリプトで書いていくのね。

[shell] localで複数宣言

複数宣言する際に、配列も交えて宣言ができる

myfunc(){
    local next words=() prev
    next="taiwan"
    prev="korea"
    words=("a" "b" "c")
    echo $prev
    echo ${words[@]}
}
myfunc

$ bash cli.bash
korea
a b c

これで、以下のlocalの宣言の意味もわかりますね。

_bitcoin_cli() {
    local cur prev words=() cword
    local bitcoin_cli

    # save and use original argument to invoke bitcoin-cli for -help, help and RPC
    # as bitcoin-cli might not be in $PATH
    bitcoin_cli="$1"

    COMPREPLY=()
    // 省略
}
    if ((cword > 5)); then
        case ${words[cword-5]} in
            sendtoaddress)
                COMPREPLY=( $( compgen -W "true false" -- "$cur" ) )
                return 0
                ;;
        esac
    fi

この COMPREPLY=( $( compgen -W “true false” — “$cur” ) ) は何をやっているかというと、COMPREPLYの中にcompgen … のコマンドを入れていますね。

COMMAND=()
COMMAND=( $(date -d tomorrow) )

echo ${COMMAND[@]}

つまりbitcoin_cliで打った時のコマンド処理を書き換えています。

[shell] caseとesac

while :
do
    read key
    case "$key" in
        "a" ) echo "aが入力されました";;
        "b" ) echo "bが入力されました";;
        "c" ) echo "cが入力されました";;
        "q" ) echo "終了します"
            break;;
    esac
done

exit 0

$ bash cli.bash
a
aが入力されました
b
bが入力されました
q
終了します

num=(2 3 4 5 6 7 8 9)
odds=(1 3 5 7 9)
odd=()
for i in ${num}; do
    case $i in $odds)
        odd+=($i)
        ;;
    esac
done
echo ${odd[@]}

なんか違うな〜

[shell] 配列とforループ

()で定義して、配列名[@]で渡すと、一つづつ処理される

langs=("C++", "C", "java", "rust")
for lang in ${langs[@]}; do
    echo $lang
done

数字としてキャストするには$(())と二重で囲む必要がある

num=(1 2 3 4 5)
ans=()
for i in ${num[@]}; do
    ans+=$((i*i))
done
echo ${ans[@]}

$ bash cli.bash
1491625

[shell] 関数の書き方

#!/bin/bash
say_hello() {
    echo "Hello, world!"
}

say_hello

say_hello_people() {
    echo "Hello, $1 and $2!"
}

say_hello_people taro hanako

$ bash cli.bash
Hello, world!
Hello, taro and hanako!

_bitcoin_rpc() {
    # determine already specified args necessary for RPC
    local rpcargs=()
    for i in ${COMP_LINE}; do
        case "$i" in
            -conf=*|-datadir=*|-regtest|-rpc*|-testnet)
                rpcargs=( "${rpcargs[@]}" "$i" )
                ;;
        esac
    done
    $bitcoin_cli "${rpcargs[@]}" "$@"
}

関数名の先頭にアンダーバー(_bitcoin_rpc)となっているのは衝突を防ぐため
localはローカル変数

i=10
func() {
    echo $i
    local i=12
    echo $i
}
func
echo $i