2026年8月14日 金曜日
AI時短ラボ
活用· 約24

macOSのawkはIGNORECASE=1をエラーも出さず無視する──Linux前提のワンライナーが「検出0件」で静かに通る

IGNORECASE はGNU awk(gawk)の拡張変数で、macOS標準のawk(awk version 20200816)では単なる変数代入として通る。2026年8月14日にDarwin 24.1.0で実測したところ、BEGIN{IGNORECASE=1} を付けた検索は終了コード0・標準エラー0バイトで「該当0件」を返し、警告すら出なかった。同じawkで gensub() は終了コード2で即死するのに、変数であるIGNORECASEだけが無言で素通りする。

執筆・編集:
目次

macOS標準のawkにBEGIN{IGNORECASE=1}を書いても、大文字小文字は区別されたまま走る。IGNORECASEはGNU awk(gawk)固有の拡張変数で、それ以外のawkでは「IGNORECASEという名前のただのユーザー変数に1を代入した」としか解釈されないからである。エラーも警告も出ず、終了コードは0のまま。だから検査スクリプトは落ちるのではなく、「該当0件」を返して合格に見える。 直し方は、パターン側をtolower($0) ~ /.../に書き換えるか、awkをやめてgrep -iを使うか、gawkを入れて明示的にgawkと呼ぶかの3つである。先に確かめるべきはawk --versionの1行で、awk version 20200816のように日付だけが返るならgawkではない。

  1. IGNORECASEはgawk拡張である。GNU公式マニュアルは「The variables that are specific to gawk are marked with a pound sign (‘#’). These variables are gawk extensions. In other awk implementations ... they are not special.」と書き、IGNORECASEをその印付きで列挙している(Edition 5.4、2026年8月14日確認)
  2. 落ちないことが問題である。 2026年8月14日にmacOS 15.1.1(Darwin 24.1.0・arm64)で実測したところ、gensub()は終了コード2とawk: calling undefined function gensubで即死するのに、IGNORECASE=1は終了コード0・標準エラー0バイトで素通りした。関数は落ち、変数は黙る
  3. macOS標準awkのman page(フッタ日付2020-11-24)が列挙する特殊変数は16個で、そこにIGNORECASEは無い。同じ環境でgawktimeoutgtimeoutはいずれも見つからなかった

症状:何も起きない

2026年8月14日、macOS 15.1.1で実行したものをそのまま貼る。まず、GNU/Linux上のgawkならHelloにマッチするはずのワンライナーである。

$ printf 'Hello\nWORLD\n' | awk 'BEGIN{IGNORECASE=1} /hello/{print "matched:"$0}'
$ echo $?
0

出力が1行も無い。 標準エラーへのリダイレクトも試したが、書き出されたのは0バイトだった。

$ printf 'Hello\nWORLD\n' | awk 'BEGIN{IGNORECASE=1} /hello/{print "matched:"$0}' 2>/tmp/awk_err.txt
$ wc -c </tmp/awk_err.txt
       0

代入自体は成功している。変数として読み出せば1が入っている。

$ awk 'BEGIN{IGNORECASE=1; print "IGNORECASE=" IGNORECASE}'
IGNORECASE=1

$ awk 'BEGIN{IGNORECASE=1; if("HELLO" ~ /hello/) print "match"; else print "nomatch"}'
nomatch

つまり「代入は通る、しかし正規表現の挙動は何も変わらない」。大文字小文字を合わせれば当然マッチするので、スクリプト全体としては正常に動いているように見える。

$ printf 'Hello\nWORLD\n' | awk 'BEGIN{IGNORECASE=1} /Hello/{print "matched:"$0}'
matched:Hello

検出漏れが「合格」に見える形

実害が出るのは、禁止語や表記ゆれを探す検査スクリプトである。次は当サイトが本記事のために組んだ最小再現で、原稿から自己肯定的な表現を拾う想定にした。入力は2行で、うち1行にASCIIの大文字が混じっている。

$ cat check.txt
この記事は最適な構成です。
これでOKです。

GNU前提で書いた版と、POSIX の範囲で書いた版を、同じ入力に当てる。

$ awk 'BEGIN{IGNORECASE=1} /最適|これでok/{c++; print "NG: " $0} END{print "該当 " c+0 " 件"}' check.txt
NG: この記事は最適な構成です。
該当 1 件
$ echo $?
0

$ awk 'tolower($0) ~ /最適|これでok/{c++; print "NG: " $0} END{print "該当 " c+0 " 件"}' check.txt
NG: この記事は最適な構成です。
NG: これでOKです。
該当 2 件
$ echo $?
0

前者は1件、後者は2件。どちらも終了コードは0である。

この差が厄介なのは、検査が全滅していない点にある。「最適」のように大文字小文字の概念が無い日本語のパターンは前者でも正しく当たるので、画面にはNG:の行が出て、スクリプトは仕事をしているように見える。落ちているのはこれでokという、ASCIIを含むパターンだけである。全部壊れていれば気づくが、一部だけ壊れているので気づかない。 ASCII を含むパターンだけを取り出すと、検出は0件になる。

$ printf 'これでOKです\n' | awk 'BEGIN{IGNORECASE=1} /これでok/{print "NG:"$0}'
$ printf 'これでOKです\n' | /usr/bin/grep -i "これでok"
これでOKです

コマンドライン経由の-vで渡しても結果は同じだった。

$ awk -v IGNORECASE=1 '/これでok/{print "NG: " $0}' check.txt
$

関数は落ちるのに、変数は黙る

同じawkでも、gawk拡張の関数を呼べば即座に落ちる。これが「変数だけが静かに失敗する」ことの理由でもある。

$ awk 'BEGIN{print gensub(/a/,"X","g","banana")}'
awk: calling undefined function gensub
 source line number 1
$ echo $?
2

$ awk 'BEGIN{a[1]="b"; print asort(a)}'
awk: calling undefined function asort
 source line number 1
$ echo $?
2

2026年8月14日に同一環境で取った結果を並べると、境目がはっきりする。

書いたもの 種別 終了コード 標準エラー
IGNORECASE=1 変数 0 (0バイト)
/\ycat\y/(単語境界) 正規表現演算子 0 (0バイト)
gensub(...) 関数 2 awk: calling undefined function gensub
asort(...) 関数 2 awk: calling undefined function asort

未定義の関数呼び出しは構文上「未定義」と判定できるが、未知の変数名への代入は文法的に完全に正当である。 awkの変数は宣言せずに使えるので、処理系から見たIGNORECASE=1FOO=1と区別が付かない。だから警告のしようがない。単語境界\yも同様に、パターンとして解釈された結果マッチしないだけなので、無言で0件になった。

なお、gawk以外の実装にも入っている機能は普通に動く。この挙動を「GNU由来なら全部落ちる」と一般化はできない。

$ awk 'BEGIN{a[1]=1;a[2]=2; print length(a)}'
2
$ awk 'BEGIN{print tolower("HeLLo")}'
hello

tolower()はPOSIX awkの組み込み関数である。一方、配列を渡すlength()のほうは事情が違う。gawkマニュアルは「Applying length() to an array was standardized by POSIX in 2024.(配列に対するlength()の適用は2024年にPOSIXで標準化された)」と書いており、上で動いているawk version 20200816はその標準化より前のビルドである。「gawk拡張かどうか」と「この実装が持っているかどうか」は別の軸である。 使う前に確かめる以外に判定手段は無い。

原因:公式マニュアルが「他の実装では特別扱いされない」と明記している

GNU公式の『GAWK: Effective AWK Programming』(Edition 5.4、gawk 5.4.0以降向け。2026年8月14日確認)は、7.5.1「Built-in Variables That Control awk」の冒頭でこう宣言している。

The variables that are specific to gawk are marked with a pound sign (‘#’). These variables are gawk extensions. In other awk implementations or if gawk is in compatibility mode (see Command-Line Options), they are not special. (Any exceptions are noted in the description of each variable.)

(gawk固有の変数にはポンド記号('#')を付けてある。これらはgawkの拡張である。他のawk実装においては、またはgawkが互換モードにある場合は、これらの変数は特別扱いされない。例外があれば各変数の説明に注記してある)

そして同じ一覧でIGNORECASEIGNORECASE #と、まさにその印付きで載っている。定義はこうである。

If IGNORECASE is nonzero or non-null, then all string comparisons and all regular expression matching are case-independent.

(IGNORECASEが0でないか空でなければ、すべての文字列比較とすべての正規表現マッチングが大文字小文字を区別しなくなる)

3.8「Case Sensitivity in Matching」でも、この機能はgawk限定として導入されている。

Another method, specific to gawk, is to set the variable IGNORECASE to a nonzero value (see Predefined Variables).

(もう1つの方法はgawk固有のもので、変数IGNORECASEに0以外の値を設定することである)

「他の実装では特別扱いされない」とは、実務上「代入は成功し、何も起こらない」という意味になる。マニュアルはエラーになるとも警告が出るとも書いていない。

一方、macOS側のawk(1) man page(フッタ日付2020-11-24)にはIGNORECASEという文字列が1回も出てこない。「Variable names with special meanings:」として列挙されているのは次の16個だけである。

ARGC  ARGV  CONVFMT  ENVIRON  FILENAME  FNR  FS  NF
NR    OFMT  OFS      ORS      RLENGTH   RS   RSTART  SUBSEP

このman pageはSEE ALSOでAho・Kernighan・Weinberger『The AWK Programming Language』(Addison-Wesley, 1988)を挙げている系統のawkで、SYNOPSISに並ぶオプションも-F fs-v var=value-f progfileの3つしかない。gawkの--lint--posixに当たる「拡張を使ったら教えてくれる」オプションが、そもそも無い。 実際に渡しても、無視されたうえで終了コードは0である。

$ awk --lint 'BEGIN{IGNORECASE=1}' </dev/null
awk: unknown option --lint ignored
$ echo $?
0
$ awk --posix 'BEGIN{IGNORECASE=1}' </dev/null
awk: unknown option --posix ignored
$ echo $?
0

検出漏れを処理系側に気づかせる手段が用意されていない。

対処:先に1行確かめる

1. 実行前に処理系を確かめる。 これが最も安い。

$ awk --version
awk version 20200816
$ which gawk
gawk not found

gawkマニュアルが載せているgawk --versionの出力例はGNU Awk 5.4.0, API 4.1, PMA Avon 8-g1, (GNU MPFR 4.2.1, GNU MP 6.3.0)で、GNUの名前とバージョン番号が並ぶ。日付8桁だけが返るならgawk拡張は使えないと判断してよい。ちなみにこのawkに-W versionを渡すとawk: unknown option -W ignoredと言われるが、「ignored」と言いながら終了コードは0のままである

2. tolower()に書き換える。 gawkマニュアル自身が、bracket式かtolower()toupper()への変換を挙げ、後者について「This works in any POSIX-compliant awk.(これはPOSIX準拠のどのawkでも動く)」と書いている。

awk 'tolower($0) ~ /pattern/ { ... }'

3. awkを使わない。 行を拾うだけならgrep -iで足りる。macOS標準の/usr/bin/grepgrep (BSD grep, GNU compatible) 2.6.0-FreeBSD)で-iが効くことは、上の入力で確認した。

4. どうしてもgawk拡張が要るなら、awkではなくgawkと書く。 この環境のPATHでは/opt/homebrew/bin/usr/binより前に来ているが、/opt/homebrew/bin/awkというファイルは存在しない。つまりHomebrew側に何を入れても、awkと書く限り解決されるのは/usr/bin/awkである。スクリプト内のコマンド名をgawkにするか、冒頭で存在を確認して無ければ落とすほうが確実である。

command -v gawk >/dev/null || { echo "gawk が必要です" >&2; exit 1; }

5. CIとローカルでawkの実体が違う前提で書く。 Ubuntu系のランナーではawkがmawkやgawkを指すことがあり、macOSのローカルとは挙動が変わる。片方だけで通ったことを根拠にしない。

付随:同じ環境に timeout も無かった

GNU前提のワンライナーをmacOSに持ってきたときに同時に踏みやすいので、併せて記録しておく。2026年8月14日時点のこの環境では、次の3つがいずれも見つからなかった。

$ which gawk gtimeout timeout
gawk not found
gtimeout not found
timeout not found
$ type timeout
timeout not found

timeoutはGNU Coreutils(本記事参照時点のマニュアルは9.11)のコマンドで、公式は「timeout runs the given command and kills it if it is still running after the specified time interval.(指定した時間が過ぎてもまだ実行中なら、そのコマンドを終了させる)」と定義し、終了ステータスについて「124 if command times out, and --preserve-status is not specified」と書いている。この124を判定に使っているスクリプトは、macOSではtimeout自体が無いので判定以前に止まる。

なお、この環境にはHomebrew自体は入っている(/opt/homebrew/bin/brew)。「Homebrewがあること」と「coreutilsが入っていること」は別である。 当サイトはcoreutilsを入れていないため、導入後にtimeoutgtimeoutのどちらの名前で使えるようになるかは本記事では検証していない。確実なのは、入れる前のこの環境ではどちらの名前でも見つからないという点だけである。

依存を増やさない範囲の代替としては、macOSに標準で入っている/usr/bin/perlが使える。実際に走らせて打ち切りを確認した。

$ time perl -e 'alarm 2; exec @ARGV' sleep 30
perl -e 'alarm 2; exec @ARGV' sleep 30  0.01s user 0.02s system 1% cpu 2.028 total
$ echo $?
142

30秒のsleepが約2.03秒で打ち切られ、終了コードは142(128+SIGALRMの14)だった。timeoutの124とは値が違うので、終了コードで分岐しているなら、そこも一緒に直す必要がある。

観測条件(2026年8月14日・1環境)

本記事のコマンド出力は、すべて2026年8月14日に次の1台で取得した。

項目
OS macOS 15.1.1(ProductVersion)/ビルド 24B91
カーネル Darwin 24.1.0(arm64)
awk /usr/bin/awk(318,912バイト、更新日2024年11月15日)
awk --version awk version 20200816
man page フッタ 2020-11-24 AWK(1)
gawk / timeout / gtimeout いずれも未インストール

ローカルのman awkのフッタ日付(2020-11-24)は、出典に挙げたオンラインのman pageミラーのフッタ日付と一致した。特殊変数の一覧も両者で同じ16個で、どちらにもIGNORECASEは含まれていない。ローカルとオンラインでIGNORECASEの出現回数はともに0だった。

この件の元記録は、当サイトの作業ログにあった「macOS に timeout が無い/BSD awk は IGNORECASE が効かない」という1行のメモである。終了コードや標準エラーの中身は残っていなかったため、本記事のために同日に上記のコマンドを改めて実行して取り直した。

正直な但し書き

gawk側の挙動は、この環境で実行して確かめてはいない。 この環境にgawkが入っていないため、「gawkならIGNORECASE=1Helloにマッチする」という部分は公式マニュアルの記述に基づく記載であり、当サイトが実行して確認した結果ではない。実行して確認したのは、macOS標準のawkでマッチしないことの側だけである。

「BSD awk」は通称である。 macOSの/usr/bin/awkは、man pageがSEE ALSOでAho・Kernighan・Weinbergerの原典を挙げていることから分かるとおり、いわゆるone true awk(BWK awk)系の実装で、BSD系OSが採用してきたためにこう呼ばれることが多い。本記事はawk version 20200816という1つのバージョンでの観測であり、他のバージョンや他のBSD系OSで同じとは限らない。

「エラーも警告も出ない」は、この1コマンドについての観測である。 標準エラーが0バイトだったことは確認したが、あらゆるロケール・あらゆる入力で無言であることを網羅的に確かめたわけではない。

ロケールの影響は切り分けていない。 gawkマニュアルはIGNORECASEの大文字小文字対応がロケールの文字集合に依存すると書いているが、本記事の観測はmacOS標準awkがIGNORECASEを解釈しない側の話なので、ロケールを変えた比較は行っていない。

本文でgrepをフルパスで書いているのは理由がある。 検証中、作業に使っていたシェルでgrepが別実装(ugrep 7.5.0)を呼ぶシェル関数として定義されていることにtype grepで気づいたため、grep -iの確認は/usr/bin/grepを明示して取り直した。ユーザーの素の.zshrcが定義していたものではなく、そのとき使っていたツール側が入れていた関数である。手元のgrepawk/usr/binのそれとは限らない。 確認するときはtype greptype awkでシェル関数やエイリアスが挟まっていないかを先に見るほうが安全である。

mawkなど他の実装は試していない。 CIで挙動が変わりうると書いたのは一般的な注意であって、当サイトが特定のランナー上で再現を取った結果ではない。

perlによる代替は打ち切りの確認までしかしていない。 alarmexecは子プロセスまで面倒を見ないので、timeout--kill-after--foregroundに相当する挙動は持たない。同等品ではなく、依存を増やさない範囲の代替として挙げている。

出典

シェア: ポスト はてブ

出典・参照資料

AIニュースの解説を動画でも

YouTubeでは注目ニュースの背景を解説し、Xでは新着記事をお知らせしています。

コメント

まだコメントはありません。最初のコメントを書いてみませんか?

AIについて聞きたいことはありますか?

質問箱で無料で受け付けています。回答は公開され、他の方の参考にもなります。

質問箱を見る →

新しい記事をメールで受け取る

AIの新しい発表を、出典付きで整理して届けます。

関連記事

AI時短ラボ
活用08.14読了26

出典URLの死活チェックで404だけ見てはいけない──1,517件を全数検査したら、HEADに404を返す公式ドキュメントと403の一次資料が出てきた

出典 ─ RFC 9110: HTTP Semanti
AI時短ラボ
活用08.14読了26

pkillはレンダプロセスを取り逃す──chrome-headless-shellの孤児36個でWindowServerが73秒無応答になった記録

出典 ─ Errors(セクション: pkill pattern matches the Claude Code process)
AI時短ラボ
活用08.14読了34

Search Console APIのsites.addはエラーを返さないのにデータは1件も読めない──permissionLevelがsiteUnverifiedUserで止まる二段構え

出典 ─ Sites: add | Search Co
AI時短ラボ
活用08.14読了32

HTTP 200・79,525バイトなのに可視テキストは416文字──「取得できた」を200で数えてはいけない

出典 ─ RFC 9110: HTTP Semanti
AI時短ラボ
活用08.14読了42

IndexNowにGoogleは参加していない──418URL送ってもGoogleには渡らない仕組みと、鍵が無効なときに返る403 UserForbiddedToAccessSite

出典 ─ IndexNow - Home(indexn
AI時短ラボ
活用08.14読了21

YouTubeのRSSフィードの404はUser-Agentのせいではなかった──UAヘッダごと削除しても200が返り、犯人はchannel_idだった

出典 ─ RFC 9110: HTTP Semanti
AIでショート動画を作り続ける工程──固定する5つと、毎回変える3つの記事画像
活用08.14読了16

AIでショート動画を作り続ける工程──固定する5つと、毎回変える3つ

出典 ─ YouTube ショートの作成を始める -
動画生成AIを数十本まとめて回す──CLIとWebの使い分けと、接続が切れた時の実ログの記事画像
活用08.14読了14

動画生成AIを数十本まとめて回す──CLIとWebの使い分けと、接続が切れた時の実ログ

出典 ─ Video generation - Ope