ラベル shell の投稿を表示しています。 すべての投稿を表示
ラベル shell の投稿を表示しています。 すべての投稿を表示

2023年1月18日水曜日

Ansible で実行中の bash script を上書きしても大丈夫なのか試してみた

表題どおりです。
1年くらい前の話題ですが、アドベントカレンダーに空きがあれば投稿しようと思って書いてました。
自分の dotfiles のインストールを ansible に少し移していこうとしてる repo で試しました。

結論は ansible のモジュールを使っていれば大丈夫です。
(モジュール内で適切に処理されてない可能性はありえるが、それはほぼないと思っていいのではないかと思います)

詳細は repo を参照してください。
https://github.com/srz-zumix/ansible_dotfiles/tree/main/roles/bash-cp-test


2022年6月7日火曜日

Jenkinsfile の sh ブロックの shellcheck をする GitHub Action をリリースしました

https://github.com/srz-zumix/jenkinsfile-shellcheck

actionlint が run の内容に対して shellcheck してくれるのをみて、Jenkinsfile の sh にもしてほしいなと思って作ってみました。

一応それっぽくできたのですが、何点か既知の問題があります。

sh ブロックの検出は AstBuilder 使ってるので問題ないのですが、行継続のエスケープが展開されて一行になるので、行番号がズレてしまうのが問題です。

ちなみに上は Groovy の文字列の記述がどう展開されるかテストしたものです。
Groovy の文字列記述には複数種類があります。ダブルクオーテーション・シングルクォーテーション、それを3つつなげて複数行記述。「$/」と「/$」で囲うことでも複数行記述できます。このうちシングルクォーテーション以外は GString にあたります。($/~/$はエスケープがそのまんま展開されるのがダブルクオーテーションと異なります)

GString は「${HOGE}」のような変数を展開します。
Jenkinsfile で GString の場合に環境変数を参照する場合に ${env.HOGE} のように書くこともあるのではないでしょうか。
(GString は知らないと結構ハマりやすいところだと思います)

AstNode から getText すると GString は展開されて取得できるのですが、そのとき「${env.HOGE}」は「env.HOGE」として展開されます。
この挙動はそんなには問題ないですが、Jenkinsfile が実行されるときのそれとは異なるので注意。


実行時と異なるという点での問題はまだあります。
sh の引数には単一の文字列ではなく、文字列連結して渡すことも当然できるため以下のようなケースで正しく解析できません。

頑張ればできないことはないかもしれませんが、今は非対応です。

ちなみに GString でシークレット環境変数を使うと展開されてから評価されるので shell 実行時に環境変数展開されるようにすべきなのですが、その警告は Jenkins がしてくれるので jenkinsfile-shellcheck ではノータッチです。
(ジョブ実行時もしくは validate で検出可能)

issue や pull request お気軽にどうぞ。
では、今回は以上です。

2022年1月11日火曜日

bash スクリプトの実行中上書き動作が ansible copy module で再現するのか試してみた

きっかけはこちら 「スーパーコンピュータシステムのファイル消失のお詫び
bash のスクリプト実行中にファイルを上書きすると、実行中の場所から上書き後の状態で実行される挙動(以下、本挙動)があり、それにより意図しない処理がされてしまったという感じ。
わかりやすい例はこの辺を参考にしてください。

Tips: 実行中のシェルスクリプトを書きかえるときには - Qiita
bash スクリプトの実行中上書き動作について

この記事は ansible で本挙動と同じことが起こり得るのかの検証記事です。



さて、結論から言うと・・・「再現しない」でした。安心してください。
仕事で ansible 使って .sh を配布してたのでちょっと心配だったんですよね。良かったです。



では、以下から検証内容を詳しく見ていきます。
(公開当初に検証用の repository が private になってました。すみませんでした)

bash のスクリプト実行中の上書き実験 by srz-zumix · Pull Request #3 · srz-zumix/ansible_dotfiles

こちらの PR で検証コードを書いて、GitHub Actions で実行してみました。
検証コードは↑で紹介した記事のサンプルスクリプト(echo foo/bar)を使っていて、copy module を使ったファイルコピーと shell module で cp コマンド使ってコピーした場合を検証しています。
このへん→https://github.com/srz-zumix/ansible_dotfiles/blob/main/roles/bash-cp-test/tasks/test-loop.yml#L19

実行中に上書きするために async を使用しています。
async タスクの完了待ちは async_status で状態を確認して待っています。
こういう書き方は普段しないので勉強になりました。

この ansible を playbook すると再現した場合は「bar」、再現しなかった場合は「foo」と表示されます。
結果はこちら


cp コマンドでは「bar」、copy module では「foo」でした。
cp コマンドの場合に再現しているので検証コードには問題がないと言えます。
よって copy module では本挙動は再現しないと言えます。
本挙動が発生するかしないかは inode が変わるか変わらないがポイントとなっており、コピー前後の inode をデバッグ出力もしています。上のスクリーンショットのログは cp コマンドの場合のログで、inode が変わってないことが見て取れます。(スクリーンショットにはありませんが、copy module では inode が変化してました)
実行結果は GitHub Actions のログでも確認できますので、詳細はそちらを参照してください。

ちなみに copy module は remote_src: true でリモートマシン内でのファイルコピーができますが、その場合も本挙動は「再現しない」です。
なぜなら copy module の実装で remote_src の true/false によらず atomic_move を使用して新しいファイルに更新しているからです。atomic_move は os.rename を使ったファイルリネームで実装されており、この atomic_move を使っていれば copy module 以外でも本挙動は発生しないと考えてよいでしょう。
(不安だったのでテスト追加した。https://github.com/srz-zumix/ansible_dotfiles/pull/4 cp コマンドの対としては remote_src のほうが適切だったと後で思った)

以上。

2019年10月16日水曜日

Docker toolbox QuickStart ショートカットを起動したときに history にゴミが溜まるのを抑制する

Docker toolbox の QuickStart でシェルを立ち上げると、ものすごい量の history が溜まって邪魔だったので対策しました。





設定でできること
HISTCONTROL
HISTCONTROL 環境変数をセットしておくと、空白から始まるコマンドや重複するコマンドを履歴に残さないようにできます。

  • ignoredups
    直前と同じコマンドの場合は保存しない
    commandhistory
    ls
    ls
    ls
    ls
    ls -a
    ls
    ls
    ls -a
    ls
  • ignorespace
    空白から始まるコマンドは保存しない
    commandhistory
    ls
    ls
    ls
    ls
    ls -a
    ls
    ls
    ls -a
    ls
  • ignoreboth
    ignoredups + ignorespace
  • erasedups
    重複するコマンドは最新のものだけが保存される
    (履歴にある重複コマンドは削除される)

自分は以下のようにしました。
export HISTCONTROL=ignoreboth

HISTIGNORE
HISTIGNORE に履歴に残したくないコマンドを列挙できます。
コマンドとコマンドの区切り文字は ':' です。

自分は以下のようにしました。
export HISTIGNORE=history:echo:'#*'

設定だけでは限界がある・・
上記の設定をしても、すこしだけ残ってほしくない履歴が減るくらいです。
自分でコマンド打つときにも便利な設定なので、しておいて損はないですが、当初の目的はこれだけでは達成できません。

というわけで、次に続きます。





start.sh を書き換える
できれば外部から非侵入的に解決したかったのですが、自分の知識ではここまでが限界でした。
Docker Toolbox のインストールディレクトリ「C:\Program Files\Docker Toolbox」に start.sh があります。
ショートカットはこちらの .sh を呼び出しており、こちらを少し書き換えることで対策しました。

ファイルの先頭で set +o history をして履歴を無効にします。
※ここで set コマンドの前に1つスペースを上げていることに注意。最初の設定で空白で始まるコマンドを履歴に残さないようにしたので
set +o history
#!/bin/bash
 set +o history

そして、ファイルの末尾あたりで set -o history で履歴を有効にします。
bash の呼び出しあたりは先頭空白を入れて、HISTCONTROL による抑制が効くようにしました。
set -o history

 if [ $# -eq 0 ]; then
   echo "Start interactive shell"
   exec "$BASH" --login -i
 else
   echo "Start shell with command"
   exec "$BASH" -c "$*"
 fi


最後に
これで設定は以上です。
きれいになりました。