フィールド分割に一時IFSを使用できるのはいつですか?


19

bashで、あなたが持っていると言ってvar=a.b.c.から:

$ IFS=. printf "%s\n" $var
a.b.c

ただし、IFS配列の作成中にこのような使用法は有効になります。

$ IFS=. arr=($var)
$ printf "%s\n" "${arr[@]}"
a
b
c

これは非常に便利ですが、これはどこに文書化されていますか?Bashドキュメントの配列またはワード分割のセクションをすばやく読んでも、どちらの方法も示されていません。単一ページのドキュメントIFSを検索してもこの効果に関するヒントは提供されません。

いつ信頼できるかわからない:

IFS=x do something

そして、それIFSがフィールド分割に影響することを期待してください。

回答:


28

基本的な考え方は、いつ外部コマンドであるかを実行するようにVAR=VALUE some-command設定VARするというものであり、それ以上の空想はありません。この直感とシェルの動作に関する知識を組み合わせれば、ほとんどの場合、正しい答えを思い付くはずです。POSIXリファレンスは、「シェルコマンド言語」の章の「シンプルコマンド」です。VALUEsome-commandsome-command

some-command外部コマンドの場合、VAR=VALUE some-commandと同等env VAR=VALUE some-commandです。VARはの環境でエクスポートされ、some-commandシェル内のその値(または値の欠如)は変わりません。

場合some-commandである機能は、VAR=VALUE some-commandと等価であるVAR=VALUE; some-command、すなわち、場所に割り当て遺跡が機能した後には戻ってきた、と変数を環境にエクスポートされません。その理由は、Bourneシェルの設計(およびその後の下位互換性)に関係しています。関数の実行前後に変数値を保存および復元する機能がありませんでした。関数はシェル自体で実行されるため、変数をエクスポートしないことは意味があります。ただし、ksh(ATT ksh93とpdksh / mkshの両方を含む)、bash、およびzsh VARは、関数の実行中にのみ設定される、より有用な動作を実装します(エクスポートされます)。ではkshの関数はkshの構文で定義されている場合、これが行われますfunction NAME …標準の構文で定義されている場合ではありませんNAME ()bash(で実行すると、これがないPOSIXモードでは、唯一のbashモードで実行されますPOSIXLY_CORRECT=1)。でzshの場合、これが行われているposix_builtinsオプションが設定されていません。このオプションはデフォルトでは設定されていませんが、emulate shまたはでオンになっていemulate kshます。

場合some-command組み込みがあり、行動は組み込みの種類によって異なります。特別なビルトインは関数のように動作します。特別なビルトインは、状態シェルに影響するため、シェル内に実装する必要があるものです(例:break制御フロー、cd現在のディレクトリ、set位置パラメータおよびオプションに影響します)。他のビルトインは、パフォーマンスと利便性のためだけにビルトインされ(ほとんどの場合、たとえば、bash機能はビルトインprintf -vによってのみ実装できます)、外部コマンドのように動作します。

割り当てはので、もし、エイリアス展開の後に起こるsome-command別名、何が起こるかを見つけるために、最初にそれを展開します。

すべての場合において、コマンドライン自体の変数置換を含め、コマンドラインの解析後に割り当てが実行されることに注意してください。だから、var=a; var=b echo $var印刷しaているため、$var割り当てが行われる前に評価されます。したがってIFS=. printf "%s\n" $var、古いIFS値を使用して分割し$varます。

すべてのタイプのコマンドを説明しましたが、もう1つのケースがあります。実行するコマンドがない場合、つまり、コマンドが割り当て(および場合によってはリダイレクト)のみで構成されている場合です。その場合、割り当てはそのまま残ります。VAR=VALUE OTHERVAR=OTHERVALUEはと同等VAR=VALUE; OTHERVAR=OTHERVALUEです。そのため、IFS=. arr=($var)IFS設定され.たままになります。既に新しい値を持っていることを期待して$IFSへの代入で使用できるためarr、の新しい値がIFSの展開に使用されるのは理にかなっています$var

要約するとIFS一時的なフィールド分割にのみ使用できます。

  • 新しいシェルまたはサブシェルを開始する(たとえば、の値に含まれる文字が2 文字未満の場合に動作が異なることを除いてthird=$(IFS=.; set -f; set -- $var; echo "$3")、複雑な方法です)。third=${var#*.*.}var.
  • kshでは、IFS=. some-functionwhere some-functionはksh構文で定義されますfunction some-function …
  • IFS=. some-function互換モードではなくネイティブモードで動作している限り、bashおよびzshで。

「」にIFS設定されたまま.です。最初の部分を読んだ後、それは理にかなっていますが、このQを投稿する前に、私はそれを期待していなかったでしょう。
ムル

1
これは別の質問への答えです
16年


6

@Gillesの答えは本当に素晴らしいと彼は(詳細に)複雑な問題を説明しています。

ただし、このコマンドの理由に対する答えは次のとおりです。

$ IFS=. printf "%s\n" $var
a.b.c

動作するのは、コマンドライン全体が実行される前解析されるという単純な考えです。そして、各「単語」はシェルによって一度処理されます。
ような割り当てIFS=.は遅延します(ステップ4は最後のものです):

4.-各変数の割り当ては展開されます...

コマンドが実行される直前まで、引数内のすべての展開が最初に処理されて、この実行可能行が構築されます。

$ IFS=. printf "%s\n" a.b.c           ## IFS=. goes to the environment.
a.b.c

の値は、コマンドに引数and が与えられる前に$var「古い」IFSで拡張されます。a.b.cprintf"%s\n"a.b.c

評価

遅延の1つのレベルは次によってもたらされるかもしれませんeval

$ IFS=. eval printf "'%s\n'" \$var
a
b
c

行が解析され(1回目)、「IFS =。」次のように環境に設定されます。

$ printf '%s\n' $var

次に、これを再度解析します。

$ printf '%s\n' a b c

これを実行しました:

a
b
c

$var(ABC)は、使用中のIFSの値が分割されます.

環境

複雑でトリッキーな部分は、環境で有効なものです!!!

これは、Gillesの回答の最初の部分で非常によく説明されています。

追加の詳細。

このコマンドが実行されるとき:

$ IFS=. arr=($var)

IFSの値は現在の環境で保持されます、はい:

$ printf '<%s>  ' "${arr[@]}" "$IFS"
<a>  <b>  <c>  <.> 

単一のステートメントのIFS。

しかし、それは避けることができます:単一のステートメントにIFSを設定する

$ IFS=. command eval arr\=\(\$var\)

$  printf '<%s>  ' "${arr[@]}" "$IFS"
<a>  <b>  <c>  < 
> 

2

に関する質問

var=a.b.c
IFS=. printf "%s\n" $var

コーナーケースです。

これは、シェル変数が設定される前にmacro expansionコマンドが実行されるためIFS=.です。

言い換えると、$varが展開されると、以前のIFS値がアクティブになり、にIFS設定され'.'ます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.