引用符付きの変数「$()」


12

私はこのスクリプトを書きました:

#!/bin/bash
while [ true ] 
do
    currentoutput="$(lsusb)"
    if [ "$currentoutput" != "$lastoutput" ]
    then
        echo "" date and Time >> test.log
        date +%x_r >> test.log
        lastoutput="$(lsusb)"
        lsusb >> test.log
    fi
    sleep 5
done

私は早く学習しようとしている初心者で、変数の引用符について質問がありました。

$()の間に変数を入れて取得しますが、ステートメント内でも引用符が必要なのはなぜifですか?ネストされたコマンドを作成するのですか?


5
while [ true ]が無限ループを生成することに注意してください。ただし、おそらくあなたがそれを行うと考える理由のためではありません。while [ false ] また、単一の引数で、[ ... ]その引数が空でない文字列であれば成功するため、無限ループ生成します。という名前のコマンドwhile trueを実際に実行しますtrue(常に成功します)。
-chepner

4
に変数を入れないでください$()。にコマンドを入れ$()ます。
ワイルドカード

回答:


22

引用符は「単語分割」を防ぎます。つまり、変数を空白文字(または、より正確には、スペース、タブ、およびデフォルトの$IFSシェル変数の値で定義されている改行)で複数の項目に分解します。

例えば、

$ var="one two"
$ howmany(){ echo $#;  }
$ howmany $var
2
$ howmany "$var"
1

ここで、howmany与えられた位置パラメータの数を知るだけの関数を定義します。ご覧のとおり、変数に渡される項目は2つあり、引用符を使用すると、変数内のテキストは1つのユニットとして扱われます。

これは、情報を正確に渡すために重要です。たとえば、変数にファイルへのパスが含まれており、ファイル名にパスのどこかにスペースが含まれている場合、実行しようとしているコマンドが失敗したり、不正確な結果になることがあります。$var変数を使用してファイルを作成しようとした場合、touch $var2つのファイルが作成されますが、touch "$var"1つだけです。

同じことがあなたにも当てはまります[ "$currentoutput" != "$lastoutput" ]。この特定のテストは、2つの文字列で比較を実行します。テストを実行すると、[コマンドは3つの引数(テキスト文字列、!=演算子、および別のテキスト文字列)を見る必要があります。二重引用符を保持すると、単語の分割が防止され、[コマンドはこれら3つの引数を正確に認識します。変数が引用されていない場合、どうなりますか?

$ var="hello world"
$ foo="hi world"
$ [ $var != $foo ]
bash: [: too many arguments
$ 

ここでは、単語の分割が発生し、代わりに[2つの文字列が表示されますhelloと、world続く!=、他の二つの文字列が続きますhi world。重要な点は、二重引用符がないと、変数の内容が1つのアイテム全体ではなく、個別のユニットとして理解されることです。

コマンド置換の割り当てには、二重引用符は必要ありません。

var=$( df )

あなたが持っているどこdfコマンドの出力を保存しますvar。ただし、$(...)実際に出力を個別のアイテムとして処理する場合を除き、変数とコマンド置換を常に二重引用符で囲むことをお勧めします。


サイドノートでは、

while [ true ]

一部はすることができます

while true

[引数を評価するコマンドであり、[ whatever ]中身に関係なく常に真です。対照的に、常に成功終了ステータスを返すwhile trueコマンドtrueを使用します(それがまさにそれですwhileループに必要なものです)。違いは、もう少し明確で、実行されるテストが少ないことです。また、:代わりに使用することもできますtrue

の二重引用符 echo "" date and Time一部はおそらく削除できます。空の文字列を挿入し、出力に余分なスペースを追加するだけです。それが必要な場合は、それらを自由に保管してください。ただし、この場合には特定の機能的価値はありません。

lsusb >> test.log

この部分はおそらくに置き換えることができecho "$currentoutput" >> test.logます。でlsusb既に実行された後、再度実行する理由はありませんcurrentoutput=$(lsusb)末尾の改行がある場合 出力を保持する必要がある場合-コマンドを複数回実行することで値を確認できますlsusbが、その必要はありません。外部コマンドの呼び出しが少ないほど、非組み込みコマンドの呼び出しごとにCPU、メモリ使用量、および実行時間のコストが発生するため(コマンドはおそらくメモリからプリロードされます)、より適切です。


こちらもご覧ください:


1
いい答えです。もしまだなら、についての本を書くべきbashです。しかし、最後の部分ではecho "$currentoutput" >> test.log 良い はずではありませprintf '%s\n' "$currentoutput" >> test.logか?
pLumo

2
@RoVoはい、printf移植性を優先する必要があります。ここではbash固有のスクリプトを使用してechoいるため、を使用することは許されません。しかし、あなたの観察は非常に正しいです
セルギーKolodyazhnyy

1
@SergiyKolodyazhnyy、... echoシェルがbashであることがわかっている(およびxpg_echoand posixフラグの値がわかっている)場合でも、それが完全に信頼できるかどうかはわかりません。値がまたはの-n場合-e、印刷されずに消えることがあります。
チャールズダフィー

4

currentoutput="$(lsusb)"lsusbが、それはコマンドで、変数ではありません。このステートメントが行うこと、それは実行しますlsusbコマンドをその出力をcurrentoutput変数に割り当てます。

これの古い構文は

currentoutput=`lsusb`

多くの例とスクリプトで見つけることができます

あなたの質問の他の部分に答えるのは、if [ ]構文ifがbashでどのように定義されているかだけです。詳しくはhttps://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.htmlをご覧ください


3
それ[ ]が実際にtestコマンドであると言うことは重要だと思います。if終了コードの0またはゼロ以外のテストに依存するため、他のコマンドでもステートメントを使用できます。
アーノニカル

...実際には、実行するif grep -qe "somestring" fileよりも実行する方がはるかに優れているgrep -qe "somestring" file; if [ $? = 0 ]; then ...ため、構文if [ ...の定義の一部である主張はif誤解を招くだけでなく、悪い習慣につながります。
チャールズダフィー

4

以下は外部コマンドcommandを実行し、その出力を返します。

"$(command)"

括弧/括弧がなければ、これはコマンドを実行する代わりに変数を探します:

"$variable"

違いについては$variable"$variable"するとき、これは、関連するなり$variable、スペースが含まれています。を使用"$variable"すると、コンテンツにスペースが含まれていても、変数のコンテンツ全体が単一の文字列に挿入されます。$variable変数の内容を使用する場合、複数の引数の引数リストに展開できます。


こんにちは@thomasrutter、すみません、引用符を意味しました...コメントを今すぐ編集します!
シャンカラ

0

逆になります-bash では、テストで変数を引用する必要がないように[[ ... ]]over the [ ... ]構文を使用することをお勧めします。

[bashでは、POSIXとの互換性のために、bashの下で実行されるスクリプト#!/bin/shまたはbashに移植されるスクリプトと一緒に提供されます-ほとんどの場合、[[ます。

例えば

# With [ - quotes are needed

$ foo='one two'; bar='one two'; [ $foo = $bar ] && echo "they're equal"
-bash: [: too many arguments

$ foo='one two'; bar='one two'; [ "$foo" = "$bar" ] && echo "they're equal"                                                                                                              
they're equal

# versus [[ - quotes not needed

$ foo='one two'; bar='one two'; [[ $foo = $bar ]] && echo "they're equal"
they're equal

bashそのような推奨事項はありません。それは提供して [[ ... ]]より便利にすることができたし、いくつかの機能は持ってい[ ... ]ないが、しかし、使用に問題がない[ ... ] 、正しくは
chepner

@chepner-ここでもっと明確にすべきでしょう。確かにそれはbashのマンページの明示的な推奨事項ではありませんが、[[すべてのこと[が行われ、さらに多くの場合、人をつまずかせて、失敗するだけでバグを散らかすだけの[機能を後付けしてみてください-それは言うのが正しい最も関連するbashスタイルガイドが決して使用するように助言する限り、コミュニティ推薦[[[[
シャロンブ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.