シェルスクリプトのファイルサイズがデプロイ遅延の原因となっている場合、単純なテキスト圧縮ではなく構文解析に基づく最適化が効果的です。gh_mirrors/sh1/shプロジェクトのshfmtツールが実装する構文木を活用した圧縮手法について、技術的な実装を解説します。
構文解析を活用した圧縮の仕組み
従来のテキスト圧縮とは異なり、この手法は抽象構文木(AST)を介してコード構造を理解し、機能を保持したまま不要な要素を削除します。処理フローは以下の3段階で構成されます。
- 解析:シェルスクリプトをASTに変換
- 最適化:ASTを簡略化し、冗長要素を除去
- 出力:最適化されたASTから最小化コードを生成
主要な圧縮戦略の実装
コメントの削除処理
圧縮モードではコメントを完全に除去します。以下のコード例では、シバン行を除き全コメントを削除するロジックを示します。
func (p *Compressor) removeComments(comments ...Comment) {
if p.compactMode {
for _, c := range comments {
if c.Line == 1 && c.Start == 0 {
p.output.WriteString("#!" + c.Text)
p.output.WriteByte('\n')
}
}
return
}
// 通常モードのコメント出力処理
}
空白文字の最適化
操作子周囲の空白を削除するロジックです。
func (p *Compressor) printArithExpr(expr *Expr) {
if p.compactMode {
p.printExpr(expr, false)
} else {
p.printExpr(expr, true)
}
}
パラメータ展開の短縮
変数展開の記法を最適化します。
if p.compactMode && !param.isShort {
if isValidName(param.Value + "suffix") {
param.isShort = true
p.printParam(param)
return
}
}
shfmtによる実際の圧縮例
以下は圧縮前後の比較です。
元のスクリプト
#!/bin/bash
# テストスクリプト
echo "開始"
if [ -f "$1" ]; then
echo "ファイル存在"
fi
圧縮後
#!/bin/bash
echo "開始"
if [ -f "$1" ];then echo "ファイル存在";fi
圧縮オプションの活用
shfmtの主要オプションを組み合わせて最適化します。
shfmt -c -s -o compressed.sh original.sh
-c: 空白文字の削除-s: 構文の簡略化-o: 出力ファイル指定
カスタム圧縮の拡張方法
独自の圧縮ルールを追加するための実装例です。
func (p *Compressor) enableCustomRule(ruleType int) {
if ruleType == 1 {
p.customRule = func(cmd *Command) bool {
return isRepeatedSequence(cmd)
}
}
}
// 使用例
p.enableCustomRule(1)
p.printCommand(command)
さらにgzipとの連携も可能です。プロジェクトのcoreutils.goではgzipサポートが既に実装されています。