Quantcast
Channel: Bashタグが付けられた新着記事 - Qiita
Viewing all 2813 articles
Browse latest View live

WinSCP >大きなSFTPパケット (589325935 B)を受信しました。サポートしている最大パケットサイズは102400 Bです。 >対処: .bashrcのechoをコメントアウト | 検討: ターミナル起動時のメッセージ

$
0
0
動作環境
Windows 8.1 Pro(64bit) 
WinSCP v5.15.4

接続エラー

2019-11-15_09h52_46.png

大きなSFTPパケット (589325935 B)を受信しました。サポートしている最大パケットサイズは102400 Bです。

このエラーは(.profileといった)スタートアップスクリプトによって表示されたメッセージによるものです。メッセージが""# fo""で始まっています。

.basrhcによるメッセージ

# for v3.7.1
ln -snf WPSv3.7.1 WPS && ls -l WPS
ln -snf WRFv3.7.1 WRF && ls -l WRF

上記のようなエラーを表示するようにしている。
数値計算で様々なバージョンを切替えてしようする時用。

対処

上記のecho出力をコメントアウトするとWinSCPの接続が成功するようになった。

関連

情報感謝です。

ターミナル起動時のメッセージ

ターミナル起動時(例としてbash)を検討してみる。

  • A. echoによるメッセージ
    • WinSCPでエラー
  • B. catによるメッセージ
    • WinSCPでエラー
  • C. subl (Sublime Text)によるメッセージ
    • WinSCPで正常

X転送が有効のとき、bashターミナル起動ごとにSublime Textが起動というのもどうかだが。

.bashrcの最後

### WRF v3.7.1 and v4.1.1 (2019.10.17)
subl ~/doc_WRFuse_191115 

SSL証明書発行を自動化しコマンド化

$
0
0

やったこと

頻繁にSSL証明書を作成する必要があったので、いちいち入力するのは手間ですしコマンド化しました。
コマンド化手順→ 【超簡単】1分で独自コマンドを作ってみる(Mac・Linux)

セットアップ

下記のシェルスクリプトを mycscという名前でコマンド化しておく
(コード上部の情報を自分のものに書き換えてください)

mycsc.sh
#!/bin/bash#mycsc csc = create SSL Sertificate#---実行前に下記の情報を入力してください(全て英語で)--------------------COUNTRY="国 日本ならJP"PROVINCE="都道府県"LOCALITY="市町村"ORGANIZATION="会社名"ORGANIZATION_UNITNAME="所属部署名"COMMON_NAME="自分の名前"EMAIL="メールアドレス"#---------------------------------------------------#すでに.sslディレクトリが存在すれば削除if[[-e ./.ssl ]];then
        rm-r .ssl
fi#.sslディレクトリ作成 & 移動mkdir .ssl &&cd .ssl

#20文字のパスワード生成password=`openssl rand -base64 12 | fold-w 20 | head-1`#opensslコマンドを実行し、情報を自動入力する
expect -c"
        set timeout 3
        spawn openssl req -x509 -newkey rsa:2048 -keyout keytmp.pem -out cert.pem -days 365
        expect \"Enter PEM pass phrase:\" {
         send \"${password}\n\"
         exp_continue
        } \"Country Name\" {
         send \"${COUNTRY}\n\"
         exp_continue
        } \"State or Province Name\" {
         send \"${PROVINCE}\n\"
         exp_continue
        } \"Locality Name\" {
         send \"${LOCALITY}\n\"
         exp_continue
        } \"Organization Name\" {
         send \"${ORGANIZATION}\n\"
         exp_continue
        } \"Organizational Unit Name\" {
         send \"${ORGANIZATION_UNITNAME}\n\"
         exp_continue
        } \"Common Name\" {
         send \"${COMMON_NAME}\n\"
         exp_continue
        } \"Email Address\" {
         send \"${EMAIL}\n\"
         exp_continue
        } timeout {
         exit 1
        }
"if[$?-eq 1 ];then
        echo"入力がタイムアウトしkeytemp.pemを作成できませんでした。情報が正しいか確認してください"exit
fi#opensslコマンドを実行し、情報を自動入力
expect -c"
        set timeout 3
        spawn openssl rsa -in keytmp.pem -out key.pem
        expect \"Enter pass phrase for keytmp.pem\" {
         send \"${password}\n\"
         exp_continue
        } timeout {
         exit 1
        }
"if[$?-eq 1 ];then
        echo"入力がタイムアウトしkey.pemを作成できませんでした。keytemp.pemとcert.pemを削除します"rm keytemp.pem cert.pem
        exit
fi#keytemp.pemを削除し完了rm keytmp.pem
echo"SSL証明書発行完了しました!"

使い方

SSL証明書を発行したいディレクトリに移動し、 mycscを叩くだけ

SSL証明書作成結果

bashで連想配列

$
0
0

連想配列のセット

連想配列宣言

変数を連想配列として扱うには、変数宣言で-Aオプションを利用します。

連想配列の宣言
declare-A musics

初期セット

連想配列の宣言と同時に連想配列のセットも同時に行う場合は()で囲みます。

連想配列初期セット
declare-Amusics=(["ultra soul"]="2001"["OCEAN"]="2005"["BANZAI"]="2004"["RED"]="2015"["GIFT"]="2008")

連想配列の追加

連想配列の追加は以下のような指定します。

連想配列追加
musics["ARIGATO"]="2004"

連想配列の値変更

連想配列の値を変更する場合は、追加と同じ書き方になります。

連想配列の値変更
declare-Amusics=(["OCEAN"]="2005")
musics["OCEAN"]="2005"

連想配列の削除

連想配列に設定したものを削除する場合は、unsetを利用します。

連想配列の削除
unset musics["GIFT"]

連想配列の表示

指定したキーの値を取得

連想配列から指定したキーの値を取得する場合は${連想配列変数["キー"]}のように指定します。

指定したキーの値を取得
echo"${musics["ultra soul"]}"

設定されている配列数を数える

連想配列に設定されている配列の数を数える場合は${#連想配列変数[@]}のようにします。

指定したキーの値を取得
echo"count:${#musics[@]}"

キーが存在しているか確認

連想配列に指定したキーが存在するか確認する場合は、if文に-nオプションまたは-zオプションを利用してキーを利用して連想配列から取り出せるかを確認します。

キーが存在する場合処理
declare-Amusics=(["ultra soul"]="2001")if[-n"${musics["ultra soul"]}"];then
    echo"exist"fi
if[-n"${musics["GIFT"]}"];then
    echo"not exist"fi# existのみが表示される
キーが存在しない場合処理
declare-Amusics=(["ultra soul"]="2001")if[-z"${musics["ultra soul"]}"];then
    echo"exist"fi
if[-z"${musics["GIFT"]}"];then
    echo"not exist"fi# not existのみが表示される

連想配列ループ

キーを表示する

連想配列にセットされているキーをループして表示する。
${!連想配列名[@]}を利用します。

キーを表示
declare-Amusics=(["ultra soul"]="2001"["OCEAN"]="2006")for music in"${!musics[@]}";do
    echo"${music}"done# 処理結果# ultra soul# OCEAN

値を表示する

連想配列にセットされている値をループして表示する。
${連想配列名[@]}を利用します。!マークをつけないのがポイントです。

キーを表示
declare-Amusics=(["ultra soul"]="2001"["OCEAN"]="2006")for music in"${musics[@]}";do
    echo"${music}"done# 処理結果# 2001# 2006

キーと値を同時に表示する

連想配列にセットされているキーと値のセットを同時に表示する場合は、キーを表示する時のループを利用して取り出します。

キーを表示
declare-Amusics=(["ultra soul"]="2001"["OCEAN"]="2006")for music in"${!musics[@]}";do
    echo"${music}:${musics[${music}]}"done# 処理結果# ultra soul:2001# OCEAN:2005

上記全てをまとめた結果

以上で終わりですが、上記をまとめたshellを乗せておきます。

#!/bin/bash# setdeclare-Amusics=(["ultra soul"]="2001"["OCEAN"]="2006"["BANZAI"]="2004"["RED"]="2015"["GIFT"]="2008")

musics["ARIGATO"]="2004"
musics["OCEAN"]="2005"unset musics["GIFT"]# readecho"${musics["ultra soul"]}"echo"count:${#musics[@]}"echo"# -n option"if[-n"${musics["ultra soul"]}"];then
    echo"exist"fi
if[-n"${musics["GIFT"]}"];then
    echo"not exist"fi

echo"# -z option"if[-z"${musics["ultra soul"]}"];then
    echo"exist"fi
if[-z"${musics["GIFT"]}"];then
    echo"not exist"fi# forecho"# for key loop"for music in"${!musics[@]}";do
    echo"${music}"done

echo"# for value loop"for music in"${musics[@]}";do
    echo"${music}"done

echo"# for key/value loop"for music in"${!musics[@]}";do
    echo"${music}:${musics[${music}]}"done
実行結果
2001
count:5
#-n option
exist
#-z option
not exist
#for key loop
OCEAN
ARIGATO
BANZAI
ultra soul
RED
#for value loop
2005
2004
2004
2001
2015
#for key/value loop
OCEAN:2005
ARIGATO:2004
BANZAI:2004
ultra soul:2001
RED:2015

macOSにAnacondaをインストールして、fishやzshなどの設定(2019)

$
0
0

Anacondaのインストール

pythonは僕のメイン言語じゃないので、pyenvなどを省略(絶対使い方忘れるから)
condaのダウンロードは公式HPから:https://www.anaconda.com/distribution/

各種shellの設定

conda init [shell]を利用する。

bash

/opt/anaconda3/bin/conda init bash

zsh

/opt/anaconda3/bin/conda init zsh

fish

/opt/anaconda3/bin/conda init fish

要注意

conda initはまだテストバージョンなので、バグが多い感じ
例えば、もし今のpathは/opt/anaconda3/bin/のままで、conda initを実行すると、次のようなエラーが出る(2019/11/17時点)。

- (line 1): 
begin;[y/N]:  
       ^
from sourcing file -
    called on line 61 of file /usr/local/Cellar/fish/3.0.2/share/fish/functions/eval.fish

in function'eval'
    called on line 140 of file -

in function'conda'
    called on line 145 of file -
    with parameter list 'activate base'

from sourcing file -
    called on line 6 of file ~/.config/fish/config.fish

from sourcing file ~/.config/fish/config.fish
    called during startup

Welcome to fish, the friendly interactive shell

ま、ターミナルを再起動したら治る。

おまけに

実はfishのプロファイルに追加されたのは

# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
eval /opt/anaconda3/bin/conda "shell.fish" "hook" $argv | source
# <<< conda initialize <<<

だけだ。

シェルスクリプトのちょっと実用的な数行の関数(数値と日時)

$
0
0

シェルスクリプトでたまに必要になる、ちょっとした処理を関数にしました。

たった数行の処理をわざわざ関数にする目的の一つは、スクリプトの可読性を向上するためです。例えばdate +'%F %T'nowでは後者の方が意図を理解しやすくなります。

もう一つの目的は、たまにしか使わないコマンドのオプションや文法などのナレッジを関数のコードにまとめておくことで、自分が忘れた時に見直しやすくするためです。

数値

整数(10進数)の判定

引数を 10進数の整数と判定できたら真(終了ステータス 0)を返す関数です。

# $1  整数かを判定する文字列function is_integer(){[[${1}==""]]&&return 1
  printf"%d"${1}>/dev/null 2>&1
}

printf は"%d"で整数に変換できない文字が与えられたら偽を返すのですが、空文字列の場合に真を返してしまいます。しかしこの関数では引数が空文字列の場合に偽を返したいので、先に空文字列の判定をしています。

テスト

for value in'-100''0''9999999999''1.1''-0.2''3e5''A''zz'''do
  if is_integer "${value}";then
    echo"'${value}': OK."else
    echo"'${value}' is NOT integer."fi
done

結果は以下のようになりました。

  • 正/負の整数と 0 で真を返す
  • 正/負の小数で偽を返す
  • eを使った指数表現で偽を返す
  • 16進数として評価可能な英文字で偽を返す
  • 文字列/空文字列で偽を返す
'-100': OK.
'0': OK.
'9999999999': OK.
'1.1' is NOT integer.
'-0.2' is NOT integer.
'3e5' is NOT integer.
'A' is NOT integer.
'zz' is NOT integer.
'' is NOT integer.

補足

空文字列の判定を以下のように書けば bash 以外のシェルでも使えるようになりますが、一般的なプログラム言語と同様の比較演算子==による可読性を優先しました。

[-z${1}]&&return 1

実数の判定

引数を実数と判定できたら真を返す関数です。上の"%d""%f"に変えただけです。

# $1  実数かを判定する文字列function is_numeric(){[[${1}==""]]&&return 1
  printf"%f"${1}>/dev/null 2>&1
}

テスト

for value in'-100''0''9999999999''1.1''-0.2''3e5''A''zz'''do
  if is_numeric "${value}";then
    echo"'${value}': OK."else
    echo"'${value}' is NOT numeric."fi
done

結果は以下のようになりました。

  • 正/負の整数と 0 で真を返す
  • 正/負の小数で真を返す
  • eを使った指数表現で真を返す
  • 16進数として評価可能な英文字で偽を返す
  • 文字列/空文字列で偽を返す
'-100': OK.
'0': OK.
'9999999999': OK.
'1.1': OK.
'-0.2': OK.
'3e5': OK.
'A' is NOT numeric.
'zz' is NOT numeric.
'' is NOT numeric.

日時

どれも一般的なプログラム言語の経験者が関数の名前を見ただけで、機能と引数を何となく連想できるようにしたつもりです。

現在日時

# $1  書式指定文字列(省略可)function now(){date +"${1:-%F %T}"}

今日の日付

# $1  書式指定文字列(省略可)function today(){date +"${1:-%F}"}

日時の書式設定

# $1  日時を表す文字列# $2  書式指定文字列(省略可)function format_datetime(){date-d"${1}" +"${2:-%F %T}"}

日時の加算/減算

# $1  日時を表す文字列# $2  正/負の整数(未来/過去)# $3  $2の単位 {year|month|day|hour|minute|second}# $4  書式指定文字列(省略可)function add_datetime(){if((${2}< 0 ));then
    local date_opt="$((${2}*-1))${3}s ago"else
    local date_opt="${2}${3}s"fi

  date-d"${1}${date_opt}" +"${4:-%F %T}"}

テスト

add_datetime '1999-12-31 23:59:59' 1 second
add_datetime '1999-12-31 23:59:59'-1 second
add_datetime '1999-12-31 23:59:59' 180 day '%Y/%m/%d %H:%M:%S'
add_datetime '1999-12-31 23:59:59'-180 day '%Y/%m/%d %H:%M:%S'
add_datetime '1999-12-31 23:59:59' 10 hour '%Y/%m/%d %H:%M:%S'
add_datetime '1999-12-31 23:59:59'-10 hour '%Y/%m/%d %H:%M:%S'
add_datetime '1999-12-31 23:59:59' 20 year '%Y年%m月%d日 %H時%M分%S秒'
add_datetime '1999-12-31 23:59:59'-20 year '%Y年%m月%d日 %H時%M分%S秒'

以下のように意図した結果になりました。

2000-01-01 00:00:00
1999-12-31 23:59:58
2000/06/28 23:59:59
1999/07/04 23:59:59
2000/01/01 09:59:59
1999/12/31 13:59:59
2019年12月31日 23時59分59秒
1979年12月31日 23時59分59秒

ディレクトリ移動をするbashシェルスクリプト関数

$
0
0

よく使うディレクトリへの移動を簡単にするために
シェルスクリプトの関数を作ります。

作り方

  1. シェルスクリプトの関数を作る
  2. 起動時に関数を読み込むように、bashrcに記載する
  3. bashrcを再読込する

1. シェルスクリプトの関数を作る

移動先のディレクトリを、仮にhome/myname/Documents/myworkとします。

以下のように、ディレクトリ移動をする関数を作成します。

mywork.sh
#!/bin/bashfunction mywork(){cd /home/myname/Documents/mywork
}

2. 起動時に関数を読み込むように、bashrcに記載する

~/.bashrcに以下を追記します。

~/.bashrc
source スクリプト/への/パス/mywork.sh

3. bashrcを再読込する

$source ~/.bashrc

使い方

作ったmywork関数が叩けるようになっているので、ターミナルで叩けばOK。

$ mywork # /home/myname/Documents/mywork へ移動することができる

少し機能拡張

複数ディレクトリから選択する方法

よく使うするディレクトリが複数あるときは、
以下のように書くことで実現することができます。

mywork.sh
#!/bin/bashfunction mywork(){# 移動したいディレクトリのリストlocal WORKDIRS="
        /home/myname/Documents/myworkA
        /home/myname/Documents/myworkB
    "# 配列に格納するlocal WORKINGDIR_ARRAY=(`echo${WORKDIRS}`)local DIRNUM=${#WORKINGDIR_ARRAY[@]}# ディレクトリ一覧を表示するlocal dir
    local i=0
    for dir in"${WORKINGDIR_ARRAY[@]}"do
        echo"${i}: ${dir}"i=$((i +1))done# ディレクトリの選択を促すlocal n
    read-p"Select directory number: " n

    # 入力された値をチェックし、cdコマンドで移動するif[${n}-ge 0 -a${n}-lt${DIRNUM}];then
        cd"${WORKINGDIR_ARRAY[${n}]}"else
        echo"Invalid Number"fi}

実行結果

$ mywork 
0: /home/takumi/Documents/myworkA
1: /home/takumi/Documents/myworkB
Select directory number: 0     # 0を入力する
$pwd/home/takumi/Documents/myworkA

0番を入力したので、myworkAディレクトリに移動しています。

参考にした記事

メモ: bash の set

$
0
0

参考

man set

引数

# 現在有効になっている設定値確認set-o# 現在有効になっている設定値確認 himBH のように、オプション値で表示echo$-# ジョブコントロールの有効化set-m
# 定義一覧を確認set

rpmbuild で no job controllとなる場合

スクリーンショット_2019-11-19_05-33-42.png

perl.spec
- %global parallel_tests 1
+ %global parallel_tests 0

 (略)
  %if %{parallel_tests}
      JOBS=$(printf '%%s' "%{?_smp_mflags}" | sed 's/.*-j\([0-9][0-9]*\).*/\1/')
      LC_ALL=C TEST_JOBS=$JOBS make test_harness
  %else
      LC_ALL=C make test
  %endif

別のエラーが出たけどそれは別の話。

スクリーンショット_2019-11-19_06-36-00.png

hub browse のように gitlabやgithubのremoteアドレスからWebへジャンプするalias

$
0
0

やりかた

fish

~/.config/fish/config.fish
alias gibr='git remote get-url origin | sed -e \'s/ssh:\/\/git@/https:\/\//g\'-e\'s/:[0-9]\{2,\}//g\'-e\'s/.git$//g\' | xargs -I\{\} open {}'

bash

~/.bash_profile
alias gibr='git remote get-url origin | sed -e \'s/ssh:\/\/git@/https:\/\//g\'-e\'s/:[0-9]\{2,\}//g\'-e\'s/.git$//g\' | xargs -I\{\} open {}'

使う時

$ cd{.git があるディレクトリへ移動}$ gibr

概要

リモートのgitリポジトリを参照したい時に、わざわざWebで検索していました。

githubでは、hubコマンドによってリンク出来ますが、gitlabでは対応していません。
sshで登録されたリポジトリだった場合色々と置換しなければ行けないのでalias化しました。

以上


フォルダ毎のサイズ順にソートしてmoreで見る

$
0
0
du -h -d 1 | sort -h -r | more

sortのオプション
-h, --human-numeric-sort
人間が読むことができる形式の数値を比較する (例: 2K 1G)

てのを最近知ったので。

Linuxの調査用にtop、ps、pstreeのログを吐く

$
0
0

沢山のシェルスクリプトが動くLinuxサーバで、
動作が遅くなったから調査してくれ、と言われることがちょこちょこある。

その時、sarコマンドなどでメモリ、CPU使用率は後で測れるけど、
どのスクリプトのどんなコマンドがボトルネックになっているのかがよくわからなかったので、
top, ps, pstreeを定期的に吐くようにスクリプトを組んでみる。

スクリプト

toppslog.sh
#!/bin/sh# Logファイル作成LOG_TIME=`date'+%Y%m%d_%H%M%S'`LOG_DATE=`date'+%Y%m%d'`mkdir-p /topps/${LOG_DATE}LOG_FILE="/topps/${LOG_DATE}/tp${LOGTIME}_${LOG_TIME}.log"# topecho-e"top cmd: ${LOG_TIME}\n****************">>$LOG_FILE
top -n 1 -bc>>$LOG_FILE# ps echo-e"ps cmd: ${LOG_TIME}\n*****************">>$LOG_FILE# CPU使用率順に並べる
ps auwx | LANG=C sort-k3,3  >>$LOG_FILE# pstreeecho-e"pstree cmd: ${LOG_TIME}\n*****************">>$LOG_FILE
pstree -all-p>>$LOG_FILE

cron登録

$ crontab -l | grep top
*/10 * * * * /toppslog.sh

とりあえず10分毎にログ取得。

結果

tp_20191120_122001.log
top cmd: 20191120_122001
****************
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 1254 user     20   0  110m  968  876 R 99.9  0.0   0:02.61 awk $3=="002" && $4
 1282 user     20   0  110m  976  888 R 99.9  0.0   0:02.23 awk $3=="002" && $4

~中略~


pstree cmd: 20191120_122001
*****************
init,1
  |- HOGEHOGE.sh
  |   `-awk,1254 $3=="002" && $4=="51"

topで、どんなコマンドがCPU率、メモリ使用しているかチェック。
そのPIDを元に検索して、pstreeの結果から、どのスクリプトに書かれているコマンドかをチェックができる。

SSHをよりセキュアにする方法

Linux Ubuntu aliasは.bash_aliasesに書こう

$
0
0

目的

  • Ubuntuのaliasを書く際に先輩に教えてもらった事を忘れないようにまとめる。
  • aliasの書く場所をまとめる。

aliasって?

  • これを登録しておくとユーザに合わせたコマンドのショートカットの作成が可能になる。
  • Ubuntu bashで実行する事のできるコマンドllはすでにllが入力されたらls -alFを実行するようにaliasに登録されている。
  • aliasを使いこなすと自分好みのコマンドのショートカットを作成することが可能になる。

自分好みのコマンドショートカットを登録したい!どうすればいいの?

  • 設定ファイルを直接開き記載してあげる必要がある。
  • 設定ファイルは.bash_profile.bashrcなど複数存在する。
  • コマンドllなどは.bashrcに記載されている。
  • 詳しい方法はmac ターミナル で llコマンドを 実行 できるようにするにまとめた通りである。(タイトルはmacになっていますがUbuntuでも同じ方法で実施可能です。)

aliasは専用の設定ファイルが用意されている

  • 実はalias設定のためのファイルがある。
  • 初期だと存在しないため、ユーザが作成する必要がある。
  • .bash_aliasesをユーザディレクトリ直下に作成しその中にaliasの設定を記載する事が正しい使い方であるらしい。
  • .bashrcには.bash_aliasesがユーザディレクトリ直下に存在したら読む処理が存在していた。(知らなかった)
  • 下記に.bashrcの一部抜粋した物を記載する。100行目あたりに下記の記載が存在する。

    # Alias definitions.                                                                                                                                                           # You may want to put all your additions into a separate file like                                                                                                             # ~/.bash_aliases, instead of adding them here directly.                                                                                                                       # See /usr/share/doc/bash-doc/examples in the bash-doc package.                                                                                                                                                                                                                                                                                               if[-f ~/.bash_aliases ];then. ~/.bash_aliases                                                                                                                                                          
    fi

具体的な実施方法

  • shellを先起動させるためのコマンドexec $SHELL -lをコマンドreloadで実行できるようにaliasを.bash_aliasesに登録する手順を下記に記載する。(※エディタはviをする)
  1. 下記コマンドを実行してユーザディレクトリ直下に移動する。

    $ cd
  2. 下記コマンドを実行してユーザディレクトリ直下に.bash_aliasesファイルを作成する。

    $ vi .bash_aliase
    
  3. 開いた.bash_aliasesファイルに下記の内容を追記する。

    alias reload='exec $SHELL -l'
    

< /dev/null とは何なのか

$
0
0

とあるサーバで、こんなコードを見かけました。

ssh $mainls ~      |
while read dir;do
  rsync -e ssh -avz--progress$main:~/$dir ~/ < /dev/null >>$logd/LOG.$(basename$0)_rsync.$today 2>&1
done

どうやら、対象のサーバ${main}へsshしたあと、ホームディレクトリをlsした結果を、スクリプト実行側へrsyncで同期しているようです。

ただ、このrsyncに、なぜか< /dev/nullを標準入力として渡しています。

/dev/nullは標準出力を捨てる際に、リダイレクト先として指定するパスです。

それをわざわざ標準入力として扱う理由とは何なんでしょうと思い、調べてみました。

cp -i に対して < /dev/nullを使うと、プロンプトに対してnoを自動的に入力する

rsyncに直接関係あるわけではないですが、調べてみたらこんな記事がありました。

linux - /dev/nullの入力リダイレクトの意味 - スタック・オーバーフロー

プロンプトに対する入力を </dev/null が抑制し no を入力したことになる

どうやらcp -i < /dev/nullとコマンドを打った時に、通常は上書きをして良いか聞かれるのに対して、自動的にnoを入力してくれるらしいです。

試してみます。

$ cat TESTFILE 
aaa
bbb
ccc

$ cat ABC
ABC

$ cp-iv TESTFILE ABC < /dev/null ;cp: `ABC`を上書きしてもよろしいですか(yes/no)? 
$$ cat ABC
ABC

確かに、自動的にnoが入力され、上書きが防止されています。
ちなみに、yes/noが出た時に、何もタイプせずにReturnをしてもno扱いとなります。

ただReturnするだけ
$ cp-iv TESTFILE ABC 
cp: `ABC`を上書きしてもよろしいですか(yes/no)? 
# ただReturnを押すだけでも、no扱いとなる$$ cat ABC
ABC

つまりこれは、空の入力さえあれば、何でもいいのでは?

空ファイル作って、それを標準入力で渡す
$ : > kara
cp-iv TESTFILE ABC < kara ;cp: `ABC`を上書きしてもよろしいですか(yes/no)? 
$$ cat ABC
ABC

いけました。
ちなみに、私は初めて知ったのですが、無限にyを吐き出してくれるyesコマンドというものがあるらしいです。

$ yes | head
y
y
y
y
y
y
y
y
y
y
yesコマンドの結果を標準入力で渡すと、自動的にyesが入る
$ yes | cp-iv TESTFILE ABC 
cp: `ABC`を上書きしてもよろしいですか(yes/no)? `TESTFILE` -> `ABC`$ cat ABC
aaa
bbb
ccc

こうしてみると、プロンプトのyes/noの入力も標準入力で受け付けられることがわかりました。

whileループ中のsshに< /dev/nullを使うと、ループが中断されることなく最後まで実行される

ssh絡みで、こんな記事がありました。

while内でsshを使うと1回で止まる - Miuran Business Systems

原因はsshコマンド実行に伴う標準入力の切替です。sshコマンドを実行すると、ローカルホストからの標準入力を停止し、sshで指定したリモートホストからの標準入力の受付を開始します。
つまり、上記のコードの例では、ローカルホストのファイルの読込みを終了させた上でsshコマンドを実行し、再びreadコマンドを実行しようとしているわけです。この時、既にファイルが閉じている為にwhileが終了してしまうわけです。

記事によると、sshした段階で、ローカルでwhile readに渡していた標準入力は打ち切られてしまい、そのままループが終わってしまうそうです。

参考記事から抜粋
# ① listfileの結果を受け取り、readコマンドが読み取り、line変数にセットcat listfile |
while read line
# ③ ②の後に標準入力が閉じてしまい、whileループが終わる(1回ループして終了)do
    echo${line}# ② ssh接続したあと、リモートホスト側で、echo実行。# このとき、ローカルの標準入力は閉じる。
    ssh  ${remotehost}"echo ${line}"done

1回で終わらせず、最後までループさせたい場合としては、-nオプションを使う必要があるそうです。
これは、内部的には< /dev/nullをやっているらしく、
ssh -nをmanコマンドで確認してみると、ガッツリ < /dev/nullについて書かれています。

-n      Redirects stdin from /dev/null (actually, prevents reading from stdin). 
         This must be used when ssh is run in the background. 
         A common trick is to use this to run X11 programs on a remote machine.
         For example, ssh -n shadows.cs.hut.fi emacs & will start an emacs on shad- ows.cs.hut.fi, 
         and the X11 connection will be automatically forwarded over an encrypted chan- nel. 
         The ssh program will be put in the background. (This does not work if ssh needs to ask for a password or passphrase; see also the -f option.)

日本語での説明はこちら

ざっくり書いてある内容は、

「標準入力を/dev/nullからリダイレクトする=標準入力からの読み込みを禁止した状態にする」

ということらしいです。

以下はローカルのHOSTNAMEを標準入力として渡しつつ、リモートホストでcat+リモートのHOSTNAMEをechoするサンプルです。
< /dev/null(または-nオプション)を指定すると、ローカル側の標準入力が受け取れなくなっているのがわかります。

/dev/null無し
# ローカルのHOSTNAMEをリモート側が標準入力として受け取り、catする$ echo$HOSTNAME | ssh remote 'cat; echo $HOSTNAME'local
remote

/dev/null有り
# sshが標準入力を禁止したので、ローカルのホスト名は受け取れない$ echo$HOSTNAME | ssh remote 'cat; echo $HOSTNAME'< /dev/null
remote
# nオプションも同様$ echo$HOSTNAME | ssh -n remote 'cat; echo $HOSTNAME'
remote

では、rsync -e sshは?

ここまで調べてみましたが、
では冒頭で疑問に思ったrsync -e sshはどうなのでしょう。
やはりssh経由になるから、標準入力が途中で閉じてしまい、ループが中断されるのでしょうか。

実際にターミナルで叩いて確認をしてみます。

dev/null有り
$ ssh $mainls ~ | while read dir;do  echo$dir; rsync -e ssh -avz--progress$main:~/$dir ~/ < /dev/null ;done
DDS
receiving incremental file list
sent 14 bytes  received 5443 bytes  10914.00 bytes/sec
total size is 2024791  speedup is 371.04
SHELL
receiving incremental file list
sent 12 bytes  received 986 bytes  1996.00 bytes/sec
total size is 50163  speedup is 50.26
SYS
receiving incremental file list
sent 14 bytes  received 4765 bytes  9558.00 bytes/sec
total size is 287499  speedup is 60.16

dev/null無し
$ ssh $mainls ~ | while read dir;do  echo$dir; rsync -e ssh -avz--progress$main:~/$dir ~/ ;done
DDS
receiving incremental file list
sent 14 bytes  received 5443 bytes  10914.00 bytes/sec
total size is 2024791  speedup is 371.04
SHELL
receiving incremental file list
sent 12 bytes  received 986 bytes  1996.00 bytes/sec
total size is 50163  speedup is 50.26
SYS
receiving incremental file list
sent 14 bytes  received 4765 bytes  9558.00 bytes/sec
total size is 287499  speedup is 60.16


特に< /dev/nullを指定しなくても、ループが中断されていないように見えます。
ssh接続を利用してるとはいえ、rsyncコマンドの仕様で標準入力が切り替わらないようになっているのでしょうか?
(この辺調べてもよくわからなかったので、誰か詳しい方いたらぜひとも教えて欲しいです……)

まとめ

  • < /dev/nullをyes/noと聞かれるコマンドに渡すと、自動的にno扱いにしてくれる
  • ssh接続時に< /dev/nullを指定すると、リモートホスト側の標準入力を受け取るのを禁止する
    • そのため、ローカル側の標準入力が途切れず、whileループが途切れない。
  • rsync -e sshに対しての < /dev/nullは謎。
    • sshコマンドと同様にループが途切れると思って付けた……?

おまけ

whileループ中のssh〜で紹介していた記事の中に、whileでは無くてforで書けば、sshしてもループが途切れることが無いとのことです。

ただ、whileとforの明確な違いについては不明。
(誰か詳しい方教えて下さい……)

参考

&>/dev/null と >/dev/null の違い - Qiita

linux - /dev/nullの入力リダイレクトの意味 - スタック・オーバーフロー

while内でsshを使うと1回で止まる - Miuran Business Systems

rsyncのサンプル例集 - Qiita

mysqlとかのサービス起動を待機するBashコマンド

$
0
0

Dockerコンテナ起動は速いですが、コンテナ内のサービスが起動完了するのに時間がかかり、後続の処理を連続して実行するとDB接続エラーになる事があります。

例えば、mysqlコンテナを起動後、テストを実行したい場合に、mysqlサーバの起動が完了するのを待つ必要があります。

以下のコマンドでは、mysqlを起動後、mysqlの3306ポートをcurlで叩いて未起動の場合は 2秒待機(sleep)を繰り返します(until -; do -; done)。

# Run Mysql
docker run -d-p 3306:3306 -eMYSQL_RANDOM_ROOT_PASSWORD=yes mysql

# Wait for serviceuntil curl localhost:3306 > /dev/null 2>&1;do echo"mysql is unavailable - waiting"&&sleep 2 ;done
echo"mysql is up"# Executing Test# ....

ポートを確認しているだけなので、mysql以外にも応用できます。以下は、InfluxDBの例です。

# Run InfluxDB
docker run -d-p 8086:8086 influxdb

# Wait for serviceuntil curl localhost:8086 > /dev/null 2>&1;do echo"influxdb is unavailable - waiting"&&sleep 2 ;done
echo"influxdb is up"# Executing Test# ....

Github REST API v3で遊んでみた

$
0
0

GithubAPIとは

Githubでも開発者のためのAPIが利用できる。
公式サイト:https://developer.github.com/v3/

GithubAPIの使い方

HTTPリクエストを送信すると、json形式でレスポンスが帰ってくる。
以下でuserのリポジトリ一覧を取得することができる

curl -u ${USER_NAME}:${PWD} -ks https://api.github.com/users/${USER_NAME}/repos

GithubAPIで少し遊んでみた

以下のコードを書いて少し遊んでみました。

git_clone_repos.sh
#! /bin/bashjson=$(curl -u ymmmtym: -ks"https://api.github.com/users/ymmmtym/repos")count=$(($(echo$json | jq '. | length')-1))echo"Follow repos are found"echo$json | jq .[].name | nl
echo-n"clone repository number: "read i
number=$((i -1))
git clone $(echo$json | jq -r .[$number].ssh_url)echo"Done!"exit 0

github

全てのリポジトリ名を表示し、番号を入力したリポジトリをクローンする。
※sshでクローンするため、公開鍵を設定している場合のみ使用できる。

これで、web上でcloneするディレクトリのurlを調べる手間が省けた。
他にも色々遊べそう。


Zabbix API で Shell からメンテナンスを操作する(Zabbix4.4)

$
0
0

背景とか

Zabbix API で Shell からメンテナンスを操作するを見て、Zabbix4系でもGUIをポチポチしないで、API経由でZabbixのメンテナンスが作成できたら良いなと思って作りました。
Zabbix APIからのメンテナンス操作についても参考にさせて頂きました。
筆者がシェルスクリプト(bash)しか理解していないため、シェルスクリプトで書いてます。

環境

  • CentOS Linux release 7.7.1908 (Core)
  • CentOS Linux release 8.0.1905 (Core) ←動作確認用に使っただけです。
  • Zabbix 4.4.0

実行結果

まずは結果から記載します。

対象機器:c2960
メンテンナンス名:再起動のため

$ ./create_maintenance_period.sh c2960 再起動のため
 [INFO] Successfully created a maintenance period.
 [INFO] Hostname: c2960
 [INFO] Maintenance Name: 再起動のため
 [INFO] Maintenance_id: 28

Zabbixのメンテナンスタブを開くと「再起動のため」という名前でメンテナンス期間が作成されています。
SnapCrab_NoName_2019-11-23_13-17-56_No-00.jpg

さらに詳細を確認すると、コマンドを実行した時間からちょうど1時間後まで、メンテナンス時間が設定されていることが確認できます。
SnapCrab_NoName_2019-11-23_13-20-40_No-00.jpg

「ホストとホストグループ」を確認すると、今回対象となっている「c2960」がホストとして対象になっていることが確認出来ます。
SnapCrab_NoName_2019-11-23_13-20-48_No-00.jpg

シェルスクリプト

指定したホストに対して1時間のメンテナンス期間をzabbixに設定します。
上記記載の通り、CentOS7と8の両方で動作を確認しています。実行に際しroot権限は不要です。
jqを使っているので、jqのインストールが必要になります。CentOS7はデフォルトで入ってないので入れる必要があります。CentOS8はデフォで入ってたような…忘れました。
もし入っていない場合はyumなりdnfなりでインストールをお願いします。
※本当はあまり外部からダウンロードする必要があるコマンドに頼りたくないのですが、便利なものは仕方ない。。

create_maintenance_period.sh
#!/bin/bash## Create maintanance period on zabbix for specific host.# Default maintenance period is 1 hours.# If you need to change the period, change folowing values:#  - time_till=`date "+%s" -d "1 hours"`#  - "period": 3600## Usage#  Arguments:#  $1 hostname(zabbix registered)#  $2 zabbix maintenance name#    Note: $2 cannot contain any spaces.## Example# ./create_maintenance_period.sh centos8 "Sunday_maintenance"# ./create_maintenance_period.sh centos8 centos8_メモリ増設のため## Initial settingszbx_endpoint="http://zabbix4/zabbix/api_jsonrpc.php"user="arkey22"password="XXXXXXXXXXXX"time_since=`date +%s`time_till=`date"+%s"-d"1 hours"`# Get tokentoken=`curl -s-d'{
    "jsonrpc": "2.0",
    "method": "user.login",
    "params": {
        "user": "'$user'",
        "password": "'$password'"
    },
    "id": 1,
    "auth": null
}'-H"Content-Type: application/json-rpc"$zbx_endpoint | awk-F'"''{print $8}'`# Get host idhost_id=`curl -s-d'{
    "jsonrpc": "2.0",
    "method": "host.get",
    "params": {
          "output": [
            "hostid"
           ],
        "filter": {
          "host": [
                  "'$1'"
            ]
        }
    },
    "auth": "'${token}'",
    "id": 1
}'-H"Content-Type: application/json-rpc"${zbx_endpoint} | jq -r'.result[].hostid'`# Create maintenancemaintenance_id=`curl -s-d'{
  "jsonrpc": "2.0",
  "method": "maintenance.create",
  "params": {
    "name": "'$2'",
    "active_since": '$time_since',
    "active_till": '$time_till',
    "hostids": [
      "'$host_id'"
    ],
    "timeperiods": [
      {
        "period": 3600
      }
    ]
  },
  "id": 1,
  "auth": "'${token}'"
}'-H"Content-Type: application/json-rpc"${zbx_endpoint} | jq -r'.result.maintenanceids[]'`if[$?-eq 0 ];then
  /bin/echo -e"[INFO] Successfully created a maintenance period."
  /bin/echo -e"[INFO] Hostname: $1"
  /bin/echo -e"[INFO] Maintenance Name: $2"
  /bin/echo -e"[INFO] Maintenance_id: $maintenance_id"else
  /bin/echo -e"[ERROR] Failed to create maintenance period."fi

最後に

シェルスクリプトでjsonを扱うと、json内で変数を記載しても展開されなくてハマりました。
今回は変数をシングルクォーテーションで囲うことで回避しました。
また、引数としてメンテナンス名を渡す際にスペースを入れると正常に動作しませんでした。

$./create_maintenance_period.sh c2960 "Sunday maintenance"

シェルスクリプトの書き方の問題だと思うのですが、解決に至らず放置して仕様としました。

ここまでご覧いただき、ありがとうございました。

GoogleアナリティクスでQiita記事のアクセス状況を調べてみた

$
0
0

前回の記事が、わずか3日で、拙稿では過去最高となる1万アクセスを超えた。
検索サイトで上位に来たのが影響しているようだ。QiitaのSEO対策はやはり秀逸である。
image.png
どんな企業から見られていたのか? Googleアナリティクスで調べてみた。

QiitaでGoogleアナリティクスを始める手順は、Qiita公式アカウントによって解説されているので、そちらを見てもらいたい。
:point_right_tone2:https://qiita.com/Qiita/items/c7f704e3786df3aa7a11

調査手順

アナリティクスにログインし、[集客]-[すべてのトラフィック]-[チャネル]に進む。
image.png

ネットワークドメイン表示に切り替える

プライマリディメンションの [その他]からネットワークドメインを選択する。
image.png

プロバイダ経由のアクセスを除外する

ドメインが次の正規表現に一致する場合は除外するよう、アドバンスフィルタで絞り込む。

((\.ne|\.or|\.ad|nuro|zoot|jtidc|bbiq|gmobb-fix|bbexcite|commufa|gmo-isp|vmobile|mineo|msone|iij-omnibus)\.jp|\.net)$|^co.jp$

image.png

[適用]をクリックするとリストが更新され、ほぼ企業ドメインだけになるはずだ。
誰もが知っている大企業ばかり並ぶが、これは母数が多いからで当然ではある。

ページタイトルを追加する

セカンダリディメンションにページタイトルを追加する。
image.png
image.png

ドメインをwhoisする

CSV形式でエクスポートする。
image.png

CSVデータをBashスクリプトに貼り付け、jpドメインを所有する会社名を表示してみる。

Bashスクリプト
#!/bin/bashwhile read DOMAIN;do
  echo-e-n$DOMAIN"\t"
  whois -h whois.jprs.jp $DOMAIN | awk'/組織名|登録者名/{print $NF}'done<<EOF
xxxxxxx.co.jp
yyyyyy.jp
zzzzzz.co.jp
    :
EOF
実行結果
xxxxxxx.co.jp   株式会社○○製作所
yyyyyy.jp       ○○○株式会社
zzzzzz.co.jp    ○○○自動車株式会社
    :

ただし、WHOISを提供するレジストラによって出力フォーマットがバラバラなので、このスクリプトでは組織名をうまく取れないドメインもある。
WHOISの後継で応答が統一された RDAPでもいくつか試してみたが 404 Not Foundしか返ってこなかった。

curl https://rdap.gtld.jprs.jp/rdap/domain/example.co.jp | jq

アナリティクスでわかったこと

前回の障害対策記事は、官公庁や地方公共団体からのアクセスが多かった。意外と基幹業務にもAccessアプリケーションが使われているのかもしれない。
ついでに、自分用のメモとして書いたような凡記事が意外と見られていることも分かった。

シェルスクリプト bash for 文 算術式

$
0
0

bash では C言語のような for 文で記述が可能。

for-bash-example1.sh
total=0
for((i = 1; i <= 12; i++))do((total += i))done
echo"${total}"

do - done を { } で置き換えることができる。

for-bash-example2.sh
total=0
for((i = 1; i <= 12; i++)){((total += i))}echo"${total}"

シェル変数(total)の初期化をfor文の中でできる。
最後の total の表示を算術式展開$((xxx))で行うことができる。

for-bash-example3.sh
for((total = 0, i = 1; i <= 12; i++)){((total += i))}echo"${total}"echo"$((total))"

iのインクリメントを省略したら、無限ループ。

for-bash-example4.sh
for((i = 0; i <= 1;))do
  echo"Test:${i}"done

bash でしか動かないため、積極的に使わなくて良いと思う。

herokuデプロイで10分であなたのアプリを世界に公開する手順【Laravel5.8】

$
0
0

今回はherokuを使った、Laravelプロジェクトを世界に手っ取り早く公開する手順をご紹介します。

実は今回僕も初の公開作業をしながら同時にこのqiitaを書いているので、これが公開されているということは動作は保証されます笑

herokuとはPaaS(Platform as a Service)と呼ばれるサービスで、アプリケーションを実行するためのプラットフォームです。

もう少しわかりやすく説明すると、サーバやOS、データベースなどの「プラットフォーム」と呼ばれる部分を、インターネット越しに使えるようにしてくれるサービスの一つです。

レンタルサーバーと似ていると思うかもしれませんが、レンタルサーバーとPaaSは「環境を貸してくれる」という意味では一緒なので、ほぼ同じと考えても問題はありません

herokuとは?初心者でも5分で分かる基本や特徴をまるっと紹介

大まかな流れか以下の通りです。

herokuに登録

heroku CLIダウンロード

herokuでアプリ作成

PostgreSQLへの接続設定

herokuでDBマイグレーション

heroku openで世界に公開!

それではいきましょう!

環境

MacOS Mojave 10.14.5(windowsのみなさんごめんなさい🙇‍♂️)
Laravel5.8.x
MAMP5.3で作成したLaravelプロジェクト

herokuに登録

こちらより各自登録しておいてください。

https://signup.heroku.com/jp

heroku CLI

まずはherokuコマンドを入れます。

$ brew tap heroku/brew && brew install heroku

念の為バージョン確認します。

$ heroku -v
heroku/7.35.0 darwin-x64 node-v12.13.0

okですね。

ターミナルからherokuログイン

cdコマンドでlaravelプロジェクトに移動してください。

$ cd /Applications/MAMP/htdocs/kanban

kanbanは僕のプロジェクト名で、MAMPなのでhtdocs以下になっています

さあheroku CLIでログインします。

$ heroku login

以下のようなメッセージが出たらq以外を押す

heroku: Press any key to open up the browser to login or q to exit: 

すると?

スクリーンショット 2019-11-24 9.29.02.png

こんなブラウザが出てくるので、Log in押します。

Logging in... done
Logged in as xxx@xxx.xxx

ログインできた!

HerokuのHTTPサーバー用コンフィグファイル作成

$ echo "web: vendor/bin/heroku-php-apache2 public" > /Applications/MAMP/htdocs/kanban/Procfile

ここは正直自分も行う理由があやふやです(^◇^;)
ProcfileはHerokuアプリの起動時に実行するプロセスを定義するためのファイルみたいです。

herokuでアプリを作成する

$ heroku create

スクリーンショット 2019-11-24 10.09.13.png

前者がアプリのURL、後者がgitのプッシュ先になります。
これから使う大切なものなので、控えておきます。

ここからgitでherokuのリモートリポジトリにプッシュしていきます。

$ git init

これでリポジトリを作成します。
そして実はLaravelプロジェクト直下に.gitという隠しディレクトリができています。
これ超重要です。

$ git add .

git add .コマンドはリポジトリにコミットする前に行うステージングを全ファイル分やってくれます。

$ git commit -m "initial commit"

ステージングしたファイルをコミットしています。

そしたら、ここで一回.git/configを開きます。

そして、先ほどターミナルに出たgitのリポジトリアドレスも確認してください。

[remote "heroku"]
  url = https://git.heroku.com/lit-plateau-44850.git

これを追記しますが、
ここのアドレスは人によって違ってきます。

これがないとリモートリポジトリがどれかわからないので、fatal errorが出るんですよね(再送2回)

では、

$ git push heroku master

herokuのmasterリポジトリにプッシュせしめるで候。

remote: Verifying deploy... done.
To https://git.heroku.com/lit-plateau-44850.git
 * [new branch]      master -> master

このようなメッセージが出たらherokuデプロイできています。

ここからは細々した設定をしていきます。

Herokuの環境変数

$ heroku plugins:install heroku-config
$ heroku config:set DB_DATABASE=laravel
$ heroku config:push

DB名Laravelは、僕はDBローカルのMySQLでDB名がLaravelだったので、herokuでも合わせました。

Herokuにpostgresqlを作成、接続

herokuが無料で提供しているのはPostgreSQLということなので、herokuでHeroku Postgresを作成し、接続します。

$ heroku addons:create heroku-postgresql:hobby-dev
$ php -r 'preg_match("/^postgres:\/\/(.+?):(.+?)@(.+?):(.+?)\/(.*?)$/", `heroku config:get DATABASE_URL`, $matches); `heroku config:set DB_CONNECTION=pgsql DB_HOST=$matches[3] DB_PORT=$matches[4] DB_DATABASE=$matches[5] DB_USERNAME=$matches[1] DB_PASSWORD=$matches[2]`;'

herokuでマイグレーション

それでは、herokuでマイグレーションを実行します。

$ heroku run "php artisan migrate"

その後、

Migration table created successfully.

と出たら、終了です。

先ほどターミナルに出てきたアプリのURL覚えてますか?

あれが公開URLなので、緊張の入力です...

スクリーンショット 2019-11-24 10.49.50.png

公開できた😭

DBも機能してる。

ちなみにtechpitで作ったtrello風タスク管理アプリになります(https化し忘れた...)

http://lit-plateau-44850.herokuapp.com/

画像投稿機能とかtwitter Authとか入れたかったなぁ...

シェルスクリプトのちょっと実用的な数行の関数(日時 #2)

$
0
0

日時

前回に引き続きシェルスクリプトのちょっとした関数で、日時の続きです。

日時をUNIX時間に変換する

UNIX 時間は、UTC の 1970年1月1日からの経過秒数(エポック秒)です。整数値なので経過時間の計算や比較がしやすくなります。

# $1  日時を表す文字列function datetime_to_epoch(){date-d"${1}" +'%s'}

現在の日時をUNIX時間で出力する

date +'%s'」に名前を付けてスクリプトの可読性を良くするための関数です。

function epoch_now(){date +'%s'}

使用例(タイムアウトの処理)

ループ 1回ごとに現在時刻と制限時刻を比較して継続/終了の判定をする例です。UNIX 時間なので加算と比較が簡単にできます。

TIMEOUT_SECONDS=10  # タイムアウトの秒数# 現在時刻にタイムアウトの秒数を加算して制限時刻を得る。limit_time=$(($(epoch_now)+${TIMEOUT_SECONDS}))# 制限時刻まで処理を続ける。while(($(epoch_now)<=${limit_time}))do
  do_something
  sleep 1
done

UNIX時間を日時に変換

逆変換の@も忘れがちで、可読性も低いので関数にします。

# $1  日時を表す文字列# $2  書式指定文字列(省略可 省略時は'yyyy-MM-dd hh:mm:ss'で出力する)function epoch_to_datetime(){date-d"@${1}" +"${2:-%F %T}"}

日時の差分計算

前回の「日時の加算/減算」に対して、今回は 2つの日時の差分を求める関数です。端数は切り捨てられます。

# $1  出力する差分の単位 {day|hour|minute|second}# $2  日時を表す文字列# $3  日時を表す文字列 $2 < $3で正の値、$2 > $3で負の値を出力するfunction diff_datetime(){# UNIX時間に変換して、2つの日時の差分を秒単位で求める。local formula=$(($(datetime_to_epoch "${3}")-$(datetime_to_epoch "${2}")))case"${1}"in
    second  );;
    minute  ) formula+=" / 60";;
    hour    ) formula+=" / 3600";;
    day     ) formula+=" / ( 3600 * 24 )";;*)return 1 ;;esacecho"${formula}" | bc
}

月初の日付

可読性のために、前回の「日時の書式設定」を使用します。
まず書式'%Y-%m-01'を使って強制的に 1日の 0時にしてから、指定の書式があればフォーマットして出力します。

# $1  日時を表す文字列# $2  書式指定文字列(省略可 省略時は'yyyy-MM-dd'で出力する)function first_day_of_month(){local first_day=$(format_datetime "${1}"'%Y-%m-01')
  format_datetime "${first_day}""${2:-%F}"}

月末の日付

前回の「日時の加算/減算」を使用します。
月末は日が 28 〜 31 の間で変動するので、月初のように日の強制変換ができません。そのためまず翌月の月初を取得してから、その前日を求めて当月の月末にします。

# $1  日時を表す文字列# $2  書式指定文字列(省略可 省略時は'yyyy-MM-dd'で出力する)function last_day_of_month(){# 月初の1か月後 = 翌月の月初local next_1st_day=$(add_datetime "$(first_day_of_month "${1}")" 1 month)# 翌月の月初の前日 = 当月の月末
  add_datetime "${next_1st_day}"-1 day "${2:-%F}"}
Viewing all 2813 articles
Browse latest View live