[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

[shell] テストコマンドを書く

test -e auto_update.sh; echo $?

FILE="auto_update.sh"
if test -n "$FILE"; then
    echo "File is exist"
fi

$ sh test.sh
0
File is exist

インストール関連はシェルで書いて、テストもシェルで書くのね。なるほど、勉強になりますね。

[shell] Ubuntuのupdate処理を自動化

auto_update.sh

#!/bin/bash

today=$(date "+%Y%m%d")

log_DIR="./update_log"
log_FILE="./update_log/update_${today}.log"

if [! -d $log_DIR]; then
    sudo mkdir ${log_DIR}
fi

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

all_update(){
    sudo apt-get update -y
}

start_time=$(date '+%Y/%m/%d %T')

echo "#### $start_time start update ####" | sudo tee -a ${log_FILE}

all_update | sudo tee -a ${log_FILE}

end_time=$(date '+%Y/%m/%d %T')

echo "#### $end_time end update ####" | sudo tee -a ${log_FILE}

$ chmod 777 auto_update.sh
$ ls -a
. .. auto_update.sh update_log
$ sh auto_update.sh

teeは標準入力から受け取った内容を、標準出力とファイルに書き出すコマンド

ASICマイニングの基礎知識 ~ 電力 ~

### ワット(W)とは
どれだけの電圧(V)で、どれだけの電流(A)を流せたかという仕事量を表す単位
ワットすうが高いほど、大きな電気エネルギーを使っている

Aアンペア x Vボルト = Wワット

### アンペア(A)
電流、電気の流れる量

### 電圧(V)
電気の強さ
※一般家庭では、100Vが一般的、国別に見ると、Europeは220Vが多い。高電圧に変更する場合は数万円程度かかる
※マイニングには100Vと200Vではパフォーマンスに差が出ると言われている

### ワットアワー(Wh)
1時間に使用するワットアワー
1000(W) x 1(h) = 1000Wh = 1kWh

### アンペア
一般家庭では20、30アンペア
東京電力では、アンペアブレーカーの色でアンペアがわかる
赤10、桃15, 黄20、緑30、グレー40、茶色50、紫60

### コンセント
一般家庭のコンセントでは1500Wまで使えるというのが多い

### 電気料金の決まり方
基本料金(アンペア)と従量課金

### 電力目安
新電力料金目安単価(1時間に1000W使用)31円/kWh(税込)〈

### 東京電力の単価表
最初の120kWhまで(第1段階料金) 1kWh 30円00銭
120kWhをこえ300kWhまで(第2段階料金) 〃 36円60銭
上記超過(第3段階料金) 〃 40円69銭
https://www.tepco.co.jp/ep/private/plan/old01.html
リミックスポイントは1kWhが東電より8円くらい安い

### 新電力
発電と送配電は大手が担っているが、小売を新電力が参入していることが多いらしい

なるほど、マイニングには電気の知識も必要やな

[LINE] Messaging APIで、テキスト以外のレスポンスを送る

modelをTextMessageではなく、TemplateMessageに変更する
公式ドキュメントを参考にする
https://developers.line.biz/en/reference/messaging-api/#imagemap-message
SDKのモデル一覧はこちら
https://github.com/line/line-bot-sdk-php/blob/master/examples/KitchenSink/src/LINEBot/KitchenSink/EventHandler/MessageHandler/TextMessageHandler.php

  $template = array('type'    => 'confirm',
                  'text'    => 'テキストメッセージ。最大240文字',
                  'actions' => array(
                                 array('type'=>'message', 'label'=>'yes', 'text'=>'yesを押しました' ),
                                 array('type'=>'message', 'label'=>'no',  'text'=>'noを押しました' )
                                )
                );
  $message = new \LINE\Clients\MessagingApi\Model\TemplateMessage(['type' => 'template','altText'  => '代替テキスト','template' => $template]);
  $request = new \LINE\Clients\MessagingApi\Model\ReplyMessageRequest([
    'replyToken' => $replyToken,
    'messages' => [$message],
  ]);
  $response = $messagingApi->replyMessage($request);

LINE側の挙動は確認できたので、あとはMySQLのCRUDとChatGPTのレスポンスの確認

LINE Messaging APIで複数レスポンスする

複数のメッセージを送信したい場合は、messagesを配列にする。

$message = new \LINE\Clients\MessagingApi\Model\TextMessage(['type' => 'text','text' => $message->{"text"}]);
$userId = new \LINE\Clients\MessagingApi\Model\TextMessage(['type' => 'text','text' => $userId]);
$request = new \LINE\Clients\MessagingApi\Model\ReplyMessageRequest([
    'replyToken' => $replyToken,
    'messages' => [$message, $userId],
]);
$response = $messagingApi->replyMessage($request);

1つのリクエストでreplyTokenを複数作ることはできないので、以下のようにレスポンスを複数は書けない。この場合、最初のレスポンスしか表示されない。

$message = new \LINE\Clients\MessagingApi\Model\TextMessage(['type' => 'text','text' => $message->{"text"}]);
$request = new \LINE\Clients\MessagingApi\Model\ReplyMessageRequest([
    'replyToken' => $replyToken,
    'messages' => [$message],
]);
$response = $messagingApi->replyMessage($request);

$userId = new \LINE\Clients\MessagingApi\Model\TextMessage(['type' => 'text','text' => $userId]);
$request = new \LINE\Clients\MessagingApi\Model\ReplyMessageRequest([
    'replyToken' => $replyToken,
    'messages' => [$userId],
]);
$response = $messagingApi->replyMessage($request);