ここから本文です

awkでシェル変数を展開させたい(入れ子問題) 次のプログラムについて、2つ質...

アバター

ID非公開さん

2016/11/1801:09:37

awkでシェル変数を展開させたい(入れ子問題)

次のプログラムについて、2つ質問です。

cat ../../POMPA/SYAIN_MASTER | awk ’ $1==” ’ ” $(nameread USER_ID $1)” ’ ” ’


(質問1)
(ダブルクオート)(シングルクオート)(ダブルクオート)$変数(ダブルクオート)(シングルクオート)(ダブルクオート)の順番で括るのは何故でしょうか?

自分で調べた範囲では、$変数をawk内で使うと、$1、$2・・・$9のような引数だと誤判断されるのでエラーとなる。そこで、$変数をシングルクオートで括ると引数として扱われなくなり、シェル変数として展開される。この段階では、awk ’ $1==’$(nameread USER_ID $1)’ ’
ところがこれだと、シングルクオートの間にシングルクオートは置けないというルールに反するため、更に$変数の左右のシングルクオートをダブルクオートで囲う必要がある。という理解で大丈夫でしょうか?

(質問2)awk ’ $1==” $(nameread USER_ID $1)” ’ この形ではなぜダメなのでしょうか? 囲った変数の中身を文字列として出力する場合、ダブルクオートを使うことが一般的だと思うのですが・・・。


【補足】上記プログラムは、webブラウザから入力されたUSER_IDが、システム登録済の社員コードと一致するかどうかを判定するものです。(SYAIN_MASTERには、一番左のレコードに社員コードが入っています。)

初心者につき、分かりやすいご指南よろしくお願いします。

この質問は、活躍中のチエリアン・専門家に回答をリクエストしました。

閲覧数:
135
回答数:
1
お礼:
50枚

違反報告

ベストアンサーに選ばれた回答

プロフィール画像

カテゴリマスター

ryo********さん

リクエストマッチ

2016/11/1807:41:04

回答では「リテラル」と「文字列」は厳格に区別して書きます。

リテラルの例: 「"a\nb$var"」
文字列の例: 「a(改行コード)b(varの値)」 

要するに,コード中に書かれているものがリテラル,コードが評価され実際に得られたものを文字列と呼びます。


>> (ダブルクオート)(シングルクオート)(ダブルクオート)$変数(ダブルクオート)(シングルクオート)(ダブルクオート)の順番で括るのは何故でしょうか?

awk '$1=="'"$(nameread USER_ID $1)"'"' 

これは

awk
'$1=="'
"$(nameread USER_ID $1)"
'"'

こういう4つのリテラルに分割されます。2番目と4番目はシングルクオートでくくられているので,エスケープシーケンスなどの評価は一切行われません。一方1番目と3番目ではさまざまな評価が行われます。(ここでは1番目はただ「awk」という文字列になるだけですが)

$( ) 

はサブシェルを実行したあと,返り値の文字列をもとの文字列の中に埋め込みます。また,シェル引数 $1 は親から引き継いでいます。この値が「123」という文字列だったとき,引数構成は以下のようになります。

「nameread」
「USER_ID」
「123」

この実行結果が「aaa」という文字列だったときは,2番目の文字列はこうなります。

「aaa」

そうですね,"$()" と書いているので,実行結果の文字列がもう一度そのまま同じ文字列になりますね。で,4つの文字列は結果的にこうなります。

「awk」
「$1=="」
「aaa」
「"」

そして,1番目と2番目の間だけ空白で,他は詰まっていたので,コマンドが実行されるときには詰まっているところが連結されて

「awk」「$1=="aaa"」

という引数構成になります。



>> $変数をawk内で使うと、$1、$2・・・$9のような引数だと誤判断されるのでエラーとなる。そこで、$変数をシングルクオートで括ると引数として扱われなくなり、シェル変数として展開される。

評価は2段階で,まずシェルで $ が評価されてリテラルから文字列が生成され,次に文字列に含まれる $ がawkインタプリタで評価されて…という流れです。また,「ユーザが代入できない数字だけで構成される名前の特別なシェル変数が引数として使われる」という認識ですね。シェル変数と引数を区別するのはおかしいです。

✕ 変数をシングルクオートで括ると引数として扱われなくなり、シェル変数として展開される。
◯ 変数をシングルクオートで括るとシェル変数として扱われなくなり、awk実行時に1列目の値が格納された変数として扱われる。


>> ところがこれだと、シングルクオートの間にシングルクオートは置けないというルールに反するため、更に$変数の左右のシングルクオートをダブルクオートで囲う必要がある。という理解で大丈夫でしょうか?

まず根本的に分けかたがおかしいですね。ダブルクオートに囲われているのはシングルクオートではなく $( ) のほうです。なお,ダブルクオートリテラルを使う場合,$やダブルクオート自身がバックスラッシュでエスケープできるので,リテラルを分割せずに

awk "\$1==\"$(nameread USER_ID $1)\""

と書くことができます。どちらが見やすいかは好みが分かれます。



>> awk ’ $1==” $(nameread USER_ID $1)” ’ この形ではなぜダメなのでしょうか? 囲った変数の中身を文字列として出力する場合、ダブルクオートを使うことが一般的だと思うのですが・・・。

シングルクオートリテラルの中に存在するものは一切評価されないので,これは

「awk」「$1=="$(nameread USER_ID $1)"」

という引数構成で実行が走ってしまいます。




【補足: Zsh以外のシェル,例えばBashでは…】

厳密には

awk '$1=="'"$(nameread USER_ID $1)"'"' 

ではなく

awk '$1=="'"$(nameread USER_ID "$1")"'"' 

が正しいです。今回は数字のみを $1 として想定していると思いますが,もしダブルクオートで括らなかった場合,ここに任意の文字列,たとえば「1  2  3」と空白入りの文字列がきてしまった場合,外側のコマンド実行時[※1]に

「nameread」
「USER_ID」
「1」
「2」
「3」

という扱いになって,意味が変わってしまいます。

"$(nameread USER_ID "$1")"

これだけ見るとダブルクオートがネストしていて気持ち悪いかもしれませんが,$( ) の中でさらにダブルクオートがネストすることは許可されています。なれるまで違和感があるかもしれませんね。また今回は問題ありませんでしたが,$( ) の返り値についても同様で

a$(command)b

と書くと,commandの返り値が「1  2  3」と空白入りの文字列だった場合に,コマンド実行時[※1]に

「a1」
「2」
「3b」

と分割されてしまうんですよね。なのでここも

"a$(command)b"

と括るのが正しいです。


※1「コマンド実行時に」と書いたのには意味があって,代入を行う場合には括る必要はありません。

#Good
x=a$(command)b

#Bad
echo a$(command)b

  • ryo********さん

    2016/11/1807:45:47

    訂正

    この実行結果が「aaa」という文字列だったときは,2番目の文字列はこうなります。

    この実行結果が「aaa」という文字列だったときは,3番目の文字列はこうなります。

  • その他の返信を表示

返信を取り消しますが
よろしいですか?

  • 取り消す
  • キャンセル

あわせて知りたい

みんなで作る知恵袋 悩みや疑問、なんでも気軽にきいちゃおう!

Q&Aをキーワードで検索:

Yahoo! JAPANは、回答に記載された内容の信ぴょう性、正確性を保証しておりません。
お客様自身の責任と判断で、ご利用ください。
本文はここまでです このページの先頭へ

「追加する」ボタンを押してください。

閉じる

※知恵コレクションに追加された質問は選択されたID/ニックネームのMy知恵袋で確認できます。

不適切な投稿でないことを報告しました。

閉じる