0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

zsh で変数名を path にすると PATH が壊れて date も awk も消える

0
Last updated at Posted at 2026-10-23

はじめに

📝 この記事は note ・ Zenn にも投稿しています。

2026-09-14、Mac の内蔵SSDを整理していた。先にファイルの一覧(サイズ・更新時刻・パス)をテキストに書き出しておき、~/Downloads の下の画像を、サイズと更新日つきで並べようとした。

grep -E " (Downloads|Movies|Documents/2018)" media.txt | grep -v iCloud写真 \
  | while read sz mt path; do
      printf "%7.1fM %s %s\n" $((sz/1048576.0)) "$(date -r $mt +%F)" "$path"
    done

出てきたのはこれだった。

(eval):1: command not found: date
    1.0M  Downloads/ChatGPT Image 2026年6月15日 23_53_56.png
(eval):1: command not found: date
    0.1M  Downloads/名称未設定のデザイン-2.png
(eval):1: command not found: date
    0.3M  Downloads/Screenshot_20260702-220057.png

サイズとパスは出ている。日付の欄だけが空で、1行ごとに date が見つからないと言われる。同じコマンドの後ろに続けていた集計も、全部同じ形で落ちた。

(eval):1: command not found: awk
(eval):1: command not found: sort
(eval):1: command not found: sed
(eval):1: command not found: ls
(eval):1: command not found: head

path は PATH の別名

原因はループの変数名 path だった。zsh では小文字の path は配列で、大文字の PATH と結びついている。片方に代入すると、もう片方も書き換わる。

print -r -- "path: ${(t)path} / PATH: ${(t)PATH}"
# path: array-tied-special / PATH: scalar-tied-export-special

read sz mt path は1行読むたびに、3つ目の欄(ファイルのパス)を path に入れる。その瞬間に PATH もそのファイルのパスに変わり、/bin も /usr/bin も検索先から消える。

printf と $(( )) の計算は zsh の組み込みなので、PATH が無くても動く。だからサイズとパスは正しく出て、外部コマンドの date だけが消えた。壊れ方が半分だけで、行の形はそれらしく出ていた。

最小の再現

zsh 5.9(macOS 標準)で、設定ファイルを読まずに再現した。

zsh -f -c '
printf "12 2026-09-14 /tmp/a\n" | while read sz mt path; do
  print -r -- "PATH=$PATH"
  date
  awk "BEGIN{print 1}"
done
print -r -- "ループの後 PATH=$PATH"
ls /
'
PATH=/tmp/a
zsh:1: command not found: date
zsh:1: command not found: awk
ループの後 PATH=
zsh:1: command not found: ls

ループの中では PATH が /tmp/a になっている。ループを抜けたあとは空になっていた。zsh の read は入力の終わりで失敗すると、変数を空にして終わる(a=x のあとに空の入力を read a すると、終了コード1で a は空になった)。最後の1回で path が空になり、PATH も一緒に空になる。壊れた PATH はループの後ろまで残る。

同じ1行を bash 3.2(macOS 標準の /bin/bash)で流すと、date はそのまま動く。

printf "12 x /tmp/a\n" | while read sz mt path; do date +%F; done
# 2026-10-12

bash では path はただの変数で、PATH とは関係がない。bash で覚えた書き方を zsh にそのまま持ち込むと、ここで踏む。

関数の中でも消える

local path=... と関数の中に閉じ込めても、その関数の中では同じことが起きる。

f() { local path=/x; date +%F; }
f
# f: command not found: date
date +%F
# 2026-10-12

関数を抜ければ戻る。ただ、関数の中で外部コマンドを呼ぶなら、local を付けても避けられない。

結びついている名前はほかにもある

path と同じ「配列と大文字の変数が結びついた」ものは、手元の zsh 5.9 でほかに3つあった。

print -r -- "fpath: ${(t)fpath} / cdpath: ${(t)cdpath} / manpath: ${(t)manpath}"
# fpath: array-tied-special / cdpath: array-tied-special / manpath: array-tied-special
小文字 結びついている先
path PATH(コマンドの検索先)
fpath FPATH(関数の自動読み込み先)
cdpath CDPATH(cd の検索先)
manpath MANPATH(man の検索先)

実際に壊れるところまで確かめたのは path だけだ。ファイルのパスを受ける変数は、この4つを避けて fname や file にした。直したあとの同じループはこうなる。

printf "12 x /tmp/a\n" | while read sz mt fname; do date +%F; done
# 2026-10-12

パスを受け取る変数だから path と名付けた。名前としては一番素直で、それがそのまま、シェルが自分の道を探すための名前だった。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?