🤐

いますぐ Mac の CLI から Windows で文字化けしない ZIP ファイルを作りたい僕たち私たち

に公開

これはなに

  • 令和にもなって未だに macOS Finder で zip 作ったら Windows で文字化けして悲しい
  • CLI でサクッと Windows / macOS / Linux 全対応の zip ファイルを作りたい
  • ありがちな文字化けの原因は以下 2 点らしいので、それぞれ対応したい
    • macOS の Unicode 正規化形式 NFD について、一般的な NFC に寄せるのが丸いぽい
    • Windows では zip 解凍時のファイル名の解釈の仕様が「UTF-8 フラグがあれば UTF-8 で、なければ OS 標準の CP932 で解釈」になってるらしいので、UTF-8 フラグを付与しないと化け放題になるらしい

参考
ファイルアップロードではNFC/NFD問題に気をつけろ!~MacファイルシステムにおけるUnicode正規化の闇~
Windows と Unicode とボク > DLC3: ZIPファイルと文字化け
macOSのZIPコマンド

シェル関数でがんばる

システム利用ではなく、日常使いで「多くのケースで大丈夫にしたい」程度なので、シェル関数でがんばることにする。

# 文字コードのコンバーターを入れる
$ brew install convmv

# zip を brew で入れ直して --unicode オプションを使えるようにする
$ brew install zip

# パスを設定して macOS 標準 zip から brew で入れた zip を使うようにする
$ which zip # /usr/bin/zip など macOS 標準の zip であることを確認
$ brew link zip # macOS 標準 zip と衝突するため link 生成されず、PATH の書き換えコマンドが例示される
$ echo 'export PATH="/usr/local/opt/zip/bin:$PATH"' >> ~/.zshrc # ↑ の例示通り実行、環境による
$ which zip # この例では /usr/local/opt/zip/bin/zip のように brew で入れたものになるはず

ここまでやったら以下 shell function を .zshrc とかに登録。

# safezip - どの OS でも文字化けしないよう NFC / UTF-8 な zip を作成する
# brew install convmv で convmv コマンド実行でき
# brew install zip で zip を入れ直して PATH を通している前提
# e.g.) $ safezip src/ [out.zip]
safezip() (
  set -euo pipefail
  src="${1%/}"
  out="${2:-${src##*/}.zip}"
  base="${src##*/}"
  [ -e "$src" ] || { echo "❌ Not found: $src" >&2; exit 1; }

  # 出力先のパス解決のがんばり
  dest_dir="$(dirname -- "$out")"
  dest_name="$(basename -- "$out")"
  if [ "$dest_dir" = "." ]; then
    dest="$PWD/$dest_name"
  else
    dest="$(cd "$dest_dir" && pwd -P)/$dest_name"
  fi

  # 文字コード変換を非破壊的にやりたいから tmp でうにょってるだけ
  tmp="$(mktemp -d "${TMPDIR:-/tmp}/safezip.XXXXXX")"
  trap 'rm -rf -- "$tmp"' EXIT
  if [ -d "$src" ]; then
    mkdir -p "$tmp/$base" && cp -R "$src"/. "$tmp/$base"/
  else
    cp "$src" "$tmp/$base"
  fi

  cd "$tmp"

  # convmv の --nfc で NFC 正規化へコンバート
  # コンバート時にエンコーディング指定が必要なので -f utf-8 -t utf-8 をつけてる
  convmv -r -f utf-8 -t utf-8 --notest --nfc "$base"

  # -UN が --unicode オプション、ここで UTF-8 フラグをつけてる
  # ついでにありがちな .DS_Store とか消したり
  zip -r -X -UN=UTF8 "$dest" "$base" -x "*/.DS_Store" "*/__MACOSX/*"

  echo "✅ Generated: $dest"
)

つかいかた

ターミナル再起動して読み込んだら、こんな感じで使えるはず。

$ safezip target/ # ✅ Generated: ~/target.zip

... 面倒だ、OS 側でなんとかしてくれ。。。

Discussion