• ベストアンサー

UNIXシェルプログラミング ${@+"$@"}

「入門UNIXシェルプログラミング」という書籍に以下のような記述がありました。 どうも矛盾しているようで、理解できないのですが、シェルに詳しい方、解説をお願いできないでしょうか。よろしくお願い致します。 以下の文章が、もし「"$@"は""に、"$*"はNULLに置き換わります。」なら理解できるんですが・・・ 誤植では、ないですよね…? (以下引用)-------------------------------------------------------- $*をダブルクウォートで囲むと、1つの文字列になります。$@をダブルクォートで囲むと、それぞれ別々の文字列であると解釈されます。 それならいつでも、「"$@"」を使えば混乱がなくていいじゃないか、と思われるかもしれません。 ところがこれにはこれで落とし穴はあるのです。 位置パラメタに何の値もセットされていない場合、つまり、渡すべきパラメタが何もない状態のときには、"$@"はヌルに、"$*"は""に置き換わります。 ヌルの場合はパラメタとして処理しませんが、""は「何もないパラメタがある」として処理します。どちらを選択しなければならないかは、状況によって判断しなくてはなりません。 一般的には、前節で説明した${variable+value}の形式を使うのが最適でしょう。 ${@+"$@"} こう書くことで、位置パラメタに何もセットされていない場合には何もしない、という条件を作れます。

  • ouou2
  • お礼率66% (24/36)

質問者が選んだベストアンサー

  • ベストアンサー
  • notnot
  • ベストアンサー率47% (4848/10262)
回答No.2

この記述は、パラメータ無しの時の "$@" の展開が昔と今では違っていることを踏まえて無いので一部意味不明になってます。 例えば、引数が、a "b c" d という3つの文字列のとき、"$1" は a, "$2" は "b c", "$3" は d になります。 "$@" は、a "b c" d の3つの文字列になり、"$*" は、"a b c d" と1つの文字列になります。引用符で囲まず、単に $@ や $* と書くと、a b c d という4つの文字列になります。 で、引数の個数がゼロのとき、"$*" は "" (空文字列) という1つの文字列、つまり "$*" は引数の個数に関係なく常に1つの文字列になります。これに対して、"$@" は「空」つまりゼロ個の文字列に展開されます。 つまり、シェルスクリプトの引数をできるだけそのまま子供のコマンドに伝えるときには、"$@" を使えばいい訳です。これで、引数の個数がゼロ個であれ、たくさんであれ、引数が引用符で囲まれていても常に、親スクリプトの引数と同じ物、同じ個数が子コマンドに渡ります。 ところが、昔の/bin/shでは、引数がゼロ個の時の "$@" は空でなく "" という1つの空文字列に展開されるという仕様でした。このため、「できるだけそのまま子供のコマンドに伝える」という場合には、${@+"$@"} と書く必要がありました。現在のほとんどのshでは、"$@" でOKです。もしかすると古い仕様のままのshがあるかもしれませんが。

ouou2
質問者

お礼

ご回答ありがとうございます。 つまり、基本的に "$@" はNULLと解釈されるが、一部のshによっては、""と解釈される場合もあるので、「汎用性」という意味で(念のため)${@+"$@"} と書いておくのが良いという事でしょうか? この解説が、抜けている上に、"$@"と"$*"の説明に続けて説明しているので混乱を招いているという事で理解すれば良いのでしょうか…。

その他の回答 (3)

回答No.4

>「入門UNIXシェルプログラミング」 最初に見る本としては、間違えましたね。奥付をみて、なるべく最近発行された本を見ましょう。 また、プログラム言語や、フレームワークの本は、同じ種類の本を2、3冊見るようにしましょう。 辞書みたいに、何年もかけて、第一人者が書くというものではないので、表現が微妙であったり、わかりにくかったり、説明不測の所があったり、この辺に突っ込みを入れるあなただと、本意を見失ってしまう事があります。 この本の、ここで言いたいのは、業務用などプログラムするときの心構えを述べています。 つまり、 1. $* などは特殊な意味を持つが、その戻り値(参照値)は必ずしも固定されていません。 と言う話と 2. ユーザーが入力する値だったり、何かを取得して格納する変数は、初期化又は、何が入力されていても、予期しておく。 この2つを言いたかっただけです。NULLであろうがヌルであるがナルであろうが、””長さ0の文字列であろが、たいした問題ではありません。これらのために ========================= ${変数名+初期値または設定値} と言う書式があります。 ===================== と言うのが本文です。これらはオンラインヘルプの man で > man sh > man csh とかすれば、ちゃんと説明されています。ちなみにその本はちょっと硬くて、実用性がないですね。 よものであれば、リファレンスの項目がしっかりしているものを買いましょう。そうすると、上達したあとでも、役に立ちますし、今回の問題もちゃんと説明しています。

ouou2
質問者

お礼

ありがとうございます。 おかげで言語などを学ぶ際の基本的な考え方について気付かされたように思います。 また、上達したあとにも役に立つ書籍や考え方についても参考にさせて頂きたいと思います。

  • notnot
  • ベストアンサー率47% (4848/10262)
回答No.3

#2です。 まさのその理解でいいと思います。 パッケージの一部として商用Unixを含めて見知らぬ他人に広く使ってもらうなら、${@+"$@"} のほうがいいかもと言うくらいですかね。Linuxだと/bin/shはまず間違いなくbashなので心配ないですが。FreeBSDも大丈夫ですね。 なお、たまに、for i in "$@" とか for i in ${@+"$@"} というのを見かけるのですが、これは、for i と、in部を省略するのがよいです。

ouou2
質問者

お礼

丁寧にお答え頂き本当にありがとうございます。 おかげで、理解する事ができました。

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.1

"$@"は $1があれば"$1", $2があれば"$2", $3があれば"$3" .... と展開します。なので、展開できるものが無ければヌル(空)です。 "$*"は $*を展開→全体を文字列 とするものです。 他の変数を使ったもの、例えば "$i" としたのと同じです。このとき、iが空なら""(長さ0の文字列)となります。

ouou2
質問者

お礼

ご解答有難うございます。 たしかにその部分は理解できたのですが、 >${@+"$@"} >こう書くことで、位置パラメタに何もセットされていない場合には何もし >ない、という条件を作れます。 の部分がどうも納得いかないのです。

関連するQ&A

  • UNIXのシェルスクリプト

    UNIXのシェルスクリプトで以下のようなことをやりたいのですがいい方法を教えて下さい。 ファイルの中にある文字列があります。 たとえば”CURREND_DIR” この文字列をシェルスクリプトでカレントのディレクトリパスに変換したいのですが いい方法が思いつきません。 set dir_data = ‘pwd‘ sed -e ’s/CURRENT_DIR/$dir_data’ ファイル名 でできるかなと思ったのですが、”CURRENT_DIR”が”$dir_data”に置換されてしまいます。 さらにdirパスが”/”で区切られていることも問題のようです。

  • UNIX シェルスクリプト

    UNIXのシェルスクリプトを使用してテキストファイルのある列にある 特定の文字列を条件としてその行を出力するということをやりたいのですが どのようにすればできるでしょうか? 例えば,以下のようなテキストファイルがあって,2列目がYAHOOの行を 主力したいというようなことです。 AAA SOFTBANK BBB YAHOO YAHOO TEST CCC QUICK DDD YAHOO ↓ BBB YAHOO DDD YAHOO

  • シェルからファイルの内容を変更したい

    シェルを使用して複数のファイル(*.txt)中の文字列をパラメータで入力した文字列に変更したいです。その時に文字列の長さを指定して、足りないところにスペースを入れたいです。 変更前の文字列「XXXXXXXXXXXXXXXXXXX」Xが20 変更後の文字列「123               」123とスペースが17

  • UNIX のシェル 文字の置換について

    シェルでファイルの文字列を置換したいです。 /*~*/のコメントの文字なので、1行数を変えたくないです。 ─────────────────────────── ※1文字分の空白を「_」で表示します。 test.txtファイルの「XXXXX」を「aa___」に置換する。 ─────────────────────────── 【test.txt 置換前】 /**************/ /*_XXXXXYYYYY_*/ /**************/ 【シェルの内容 test.sh】 #!/bin/csh set IN_henkan = `printf "%-5s" $1` perl -i -p -e 's/XXX/'$IN_henkan'/g' test.txt 【シェルを実行】 test.sh aa 【test.txt置換前 希望する状態】 /**************/ /*_aa___YYYYY_*/ /**************/ 【test.txt置換前 実際の状態】 /**************/ /*_aa_YYYYY_*/ /**************/ ─────────────────────────── 「aa」の後ろに1文字分の空白しか挿入されないです。

  • unixのシェルでファイル内容を読み込む

    ど初心者です。わかりづらいかもしれませんが宜しくお願いします。 以下のような処理を行いたいと考えています。 (1) aaa.sh(シェルスクリプト)にてバッチプログラムを起動 (2)バッチでエラーの場合、結果ファイル(bbb.txt)に「1」を出力する。 (3)aaa.sh(シェルスクリプト)にて結果ファイルの内容を読み取り「1」の場合は、再度バッチプログラムを起動する。 このうち(3)の処理にて、結果ファイルの読み込み方法がわかりません。 調べた結果、以下のような処理で可能なようですが・・ while read LINE; do echo $line done < aaa.txt 読み込むのは1行なので、ループ処理にはしたくないのですが、例えば「read LINE aaa.txt」のような簡単なコマンドで、ファイルの中身を読み込むことはできないのでしょうか? 実はUNIXが初めてで、しかもスケジュールに余裕がないため焦っております。 なにぶん知識不足な故、ちんぷんかんぷんな質問かも知れませんが、よろしくお願いいたします。

  • シェルスクリプトについて

    シェルスクリプトを勉強しています。 hoge.txt内で"AAA"という文字列を"BBB"という文字列を検索し下記のような処理を行いたいです。  (1)AAA・BBBともにある場合  →スクリプトを続行  (2)AAA・BBBともにない場合  →スクリプトを停止  (3)AAAしかない場合  →BBBがありませんというメッセージを流す    (4)BBBしかない場合  →AAAなしでスクリプトを続行しますというメッセージを流す   シンプルなやり方でいいので上記のようなことを行いたい場合、どのようにスクリプトを書けばいいのか教えてください。 よろしくお願いします。

  • Linux シェルの trap について

    お世話になります。 シェルについて、どなたかご教授頂けますと幸いで御座います。 以下、★処理を実施していますが、シェル.sh を実行中に Ctrl+\ で終了(シグナル)しても トラップ処理が実行されません。"| tee -a ファイル名" を削除し、シェル.sh のみ実行すると、 トラップ処理が実行されます。何か、良いアイデアは御座いますか?? ******************************************************* #!/bin/bash #------------------------------------------------------------------------------ # Trap #------------------------------------------------------------------------------ trap 'echo "trapped."; トラップ処理 >> /dev/null 2>&1;exit 1' 1 3 15 trap '' 2 umask 022 : <中略> : #------------------------------------------------------------------------------ # main #------------------------------------------------------------------------------ シェル.sh | tee -a ファイル名 ★ #シェル.sh *******************************************************

  • UNIX(Bシェル)のプロンプトについて

    Bシェルのプロンプトの設定は環境変数PS1に変えたい文字列を入れればOKですが、現在自分のいるディレクトリ名を表示させたい場合はどのようにしたらいいでしょうか。 PS1="["`pwd`"]" などと設定してもディレクトリが変わったらその設定したときのディレクトリ名のままです。  また、ユーザ名も表示したいのですがsuコマンドでユーザ名を切り替えた場合プロンプトに表示させたユーザ名も変更させることはできるのでしょうか?

  • Cシェルでの文字列大小比較

    Cシェルで set AA="aa" set BB="bb" if( $AA > $BB )then echo "$AA > $BB" endif というような判定がしたいのですが、 上記の場合、”if: 条件式構文が正しくありません”というエラーが出力されます。 Cシェルの場合、文字列の比較は ifでイコールかノットイコールかは判定できますが、文字コードの大小比較が出来ないようですが、 文字列の大小比較をするにはどうすればよいのしょうか

  • シェルの配列

    シェルで2つの配列の同じ位置の文字を同時にチェックしようとして以下のコードを組みましたが、上手くいきません。$dd中の文字が配列として扱われていないのはなぜだか教えていただけますか? #! /bin/csh -f set AA="18" set BB="20" set cc = "11 12 18 19" set dd = "24 22 20 21" @ idx = 1 foreach h_code ( $cc ) echo [$AA][$h_code][$BB][$dd[$idx]]     # ccの1個目とddの1個目をそれぞれ$AA,$BBと比較 if( $AA == $h_code && $BB == $dd[$idx] ) then echo "OK" endif @ idx++ end

専門家に質問してみよう