つれづれ電脳記(日くらしPCに向かひて)

つれづれなるままに、日くらしPCに向かひて、その悪戦苦闘ぶりをそこはかとなく書き付くるおじさん。たまに雑談[管理人:goosyun]

つれづれなるままに、日くらしPCに向かひて、デジタル関係の悪戦苦闘ぶりをそこはかとなく書き付くるおじさんです。
たまに雑談してます。連絡・問い合わせフォームは、ページ最下部に置いています。[著者(運営人):goosyun]

(当ブログには本文中に広告リンクが含まれています。)

AWKで文字列を1文字ずつのフィールドに分解

 

 

 テキスト処理コマンド(ツール)のAWK(オーク)を勉強して、これまで疑問に思っていたことが解決しました。これで、少し賢くなりました。

デスクトップパソコン

1 文字列を1文字ずつに分解

 学んだことは、AWKフィールドセパレータ(フィールド区切り文字)についてです。
 文字列の1文字ずつを区切って、それぞれをフィールドにできないだろうかというのが、これまでずっと抱いていた疑問です。私はその方法があることを知りませんでした。これが、あるんですねぇ。できるんですよ!
 答えは、「カラ文字("")をフィールドセパレータにすればよい。」です。なるほど、ってなりました。方法があることが分かったので、うれしくなりました。
 ところが、ここでつまづきました。実地にやってみると、できない、実行がされないのです。
 あれこれともがき苦しんだ末に、生成AIのGeminiに相談しました。そうすると、実に明快な回答をしてくれました。そのすばらしい解説ぶり、理解しやすい説明に感心してしまいました。
 そこで、私の質問と、Geminiの解説を、ここにそっくりそのまま掲載して、皆様にご紹介します。

2【Geminiへの質問】

AWKでフィールドセパレータを指定する-Fオプションの記述方法が、よく分かりません。
 (1)  $ echo "a:i:u:e:o" | awk -F":" '{print $2,$3}'
 (2) $ echo "a:i:u:e:o" | awk -F ":" '{print $2,$3}'
上記(1)(2)どちらの記述でも実行できます。
つまり、Fの後ろの半角スペースの有無は影響しません。
ところが、
 (3)  $ echo "aiueo" | awk -F"" '{print $2,$3}'
 (4) $ echo "aiueo" | awk -F "" '{print $2,$3}'
となると、Fの後ろの半角スペースの有無で結果が異なります。
(3)では実行されないのに対し、(4)では想定どおりの実行がされます。
つまり、Fの後ろの半角スペースが必要とされます。
これは、どういう理由からでしょうか。

 

3【Geminiの回答】

 非常に鋭い着眼点ですね。 結論から申し上げますと、これはAWK自体の挙動というよりは、「シェル(Bashなど)がコマンドに引数を渡す際のルール」と「AWKが引数を解釈する順番」のミスマッチが原因です。
 一言で言うと、(3)のケースでは「プログラムそのもの('{print ...}')が区切り文字として誤って解釈されてしまっている」ために実行されません。
 なぜそうなるのか、シェルの裏側の動きを図解的に解説します。

原因のメカニズム
 シェルがコマンドを実行する際、""(空のクォート)は「空文字」を意味しますが、シェルはこれを解釈した後、クォート自体を取り除いてコマンドに渡します。
【1】 成功するケース (4) -F "" の動き
 コマンド: awk -F "" '{print $2,$3}'
 この時、シェルは以下の 3つのパーツ に分けてAWKに渡します。
 1 awk (コマンド)
 2 -F (オプション)
 3 (空文字:""の中身)
 4 '{print $2,$3}' (プログラム)
 AWKはこれを受け取り、以下のように解釈します。
・ 「お、-Fが来たな。次の引数を区切り文字にしよう」
・ → 次の引数は「空文字」だ。OK、区切り文字は「なし(1文字ずつ)」に設定する。
・ → その次の引数がプログラムだな。実行しよう。
・ 結果:成功

【2】 失敗するケース (3) -F"" の動き
 コマンド: awk -F"" '{print $2,$3}'
 ここで問題が起きます。シェルは -F"" を見ると、"" は「中身がない」ので消滅させ、前の文字と結合します。その結果、AWKに渡されるのは以下の 2つのパーツ だけになります。
 1 awk (コマンド)
 2 -F (オプション。「""」が消えて単なる -F だけが残る)
 3 '{print $2,$3}' (プログラム)
 AWKはこれを受け取り、以下のように解釈します。
・ 「お、-Fが来たな。次の引数を区切り文字に設定しなきゃ」
・ → (-Fの後ろに文字がくっついていないので、次のパーツを見に行きます)
・ → 「次の引数は '{print $2,$3}' だな。よし、これを区切り文字にしよう!」
・ → 「さて、プログラムを実行するか……あれ? もう引数がないぞ? プログラムはどこだ?」
・ 結果:エラー(または予期せぬ動作)
 つまり、(3)では実行したいプログラムのコードが、区切り文字の設定として吸い込まれてしまっているのです。

補足:なぜ (1) -F":" は動くのか?
 (1) の $ awk -F":" の場合、シェルはクォートを外して -F: というひとかたまりの文字列をAWKに渡します。
 AWKのオプション解析ルールでは、「オプション文字(F)の直後に文字がある場合、それを値として採用する」という決まりがあります。 そのため、: が即座に区切り文字として認識され、後ろのプログラム部分には影響が出ないため、正常に動作します。

まとめ
・ -F の後ろに文字がある場合(例: -F:)
   その文字が区切り文字になり、正常動作します。
・ -F の後ろが空文字の場合(例: -F"")
   シェルが "" を消してしまうため、AWKは「-F だけ」を受け取ります。
   AWKは「値が足りない」と判断し、次の引数(プログラム部分)を区切り文字として奪ってしまいます。
・ スペースを入れた場合(例: -F "")
   「-F」と「空文字」が別々の引数として渡されるため、正しく空文字が区切り文字として認識されます。

別の書き方(推奨)
 もし「空文字指定(1文字ずつ分割)」を、スペースの有無を気にせず安全に書きたい場合は、-v オプションを使って変数 FS (Field Separator) を直接指定する方法もおすすめです。
   echo "aiueo" | awk -v FS="" '{print $2, $3}'
 これなら、-F のスペース問題に悩まされることはありません。

 

それではまた次の記事で

    goosyun