単一ステートメントの IFS 設定

単一ステートメントの IFS 設定

個々のコマンド/組み込み範囲に対してカスタムIFS値を設定することが可能であることがわかります。個々の明細にカスタムIFS値を設定する方法はありますか?明らかにそうではありません。これは、次のようにすると、この操作を試みるとグローバルIFS値が影響を受けるためです。

#check environment IFS value, it is space-tab-newline
printf "%s" "$IFS" | od -bc
0000000 040 011 012
             \t  \n
0000003
#invoke built-in with custom IFS
IFS=$'\n' read -r -d '' -a arr <<< "$str"
#environment IFS value remains unchanged as seen below
printf "%s" "$IFS" | od -bc
0000000 040 011 012
             \t  \n
0000003

#now attempt to set IFS for a single statement
IFS=$'\n' a=($str)
#BUT environment IFS value is overwritten as seen below
printf "%s" "$IFS" | od -bc
0000000 012
         \n
     0000001

答え1

一部のシェル(含むbash)では:

IFS=: command eval 'p=($PATH)'

(sh / POSIXエミュレーションではない場合はbash省略できますcommand。)しかし、これは引用符なしの変数を使用する場合にも必要であり、ほとんどset -fのシェルにはローカルスコープはありません。

zshを使用すると、次のことができます。

(){ local IFS=:; p=($=PATH); }

$=PATHこれは基本的に行われない単語分割を強制することです(変数拡張にはワイルドカードもないため、shシミュレーションを除いて必要ありませんzsh)。set -f

しかしzsh$pathバンドルまたは$PATH区切り記号に分割:p=(${(s[:])PATH})またはp=("${(s[:]@)PATH}")空の要素を保持します。

(){...}(またはfunction {...})と呼ばれる。匿名関数通常、ローカル範囲を設定するために使用されます。関数ローカルスコープをサポートする他のシェルについても同様のことができます。

e() { eval "$@"; }
e 'local IFS=:; p=($PATH)'

POSIX シェルで変数とオプションのローカル範囲を指定するには、次の関数を使用することもできます。https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh。その後、次のように使用できます。

. /path/to/locvar.sh
var=3,2,2
call eval 'locvar IFS; locopt -f; IFS=,; set -- $var; a=$1 b=$2 c=$3'

(ところで、$PATH上記の分割は、zshIFSがフィールドセパレータではなくフィールドセパレータである他のシェルを除いては無効です。)

IFS=$'\n' a=($str)

a=1 b=2.

メモvar=value cmd:

存在する:

var=value cmd arg

シェルは/path/to/cmd新しいプロセスで実行され、incmdarginargv[]var=valueinを渡しますenvp[]。これは実際には変数の代入ではなく、環境変数を渡す以上のものです。処刑された注文する。 BourneまたはKornシェルでもset -k作成できますcmd var=value arg

今これは非です処刑された。 Bourneシェルでは、aloneと同様にinが最終的に設定されvar=value some-builtinます。これは、例えば(無駄な)動作が組み込みかどうかに応じて変化することを意味する。varvar=valuevar=value echo fooecho

POSIXおよび/またはkshBourneの動作は次のクラスでのみ発生するため、これを変更しました。特殊組み込み機能eval特殊内蔵型ですread。いいえ。特別でない組み込みコマンドの場合は、組み込みコマンドの実行のみが設定され、var=value builtin外部varコマンドの実行と同様に動作します。

このcommandコマンドは削除に使用できます。特別なその属性特殊組み込み機能。 POSIXが無視することは、組み込み関数の場合、evalシェルが変数スタックを実装する必要があることを意味します(コマンドを指定したりスコープを指定しなくても)。なぜなら、次のことができるからです.localtypeset

a=0; a=1 command eval 'a=2 command eval echo \$a; echo $a'; echo $a

でも:

a=1 command eval myfunction

使用、設定、呼び出すことができる関数ですmyfunction$acommand eval

(AT&Tはまだそれを実装していません)(AT&Tはまだ実装していません)ksh(仕様はほとんどに基づいています)しかし、今、これら2つを除くほとんどのシェルは実装しているので、これは実際に監督でした。他のシェルは異なる動作をします。たとえば、次のようになります。kshzsh

a=0; a=1 command eval a=2; echo "$a"

しかし。これをサポートするシェルで使用することは、localローカルスコープを実装するより信頼性の高い方法です。

答え2

KernighanとPikeの「The Unixプログラミング環境」からの標準的な保存と復元:

#!/bin/sh
old_IFS=$IFS
IFS="something_new"
some_program_or_builtin
IFS=${old_IFS}

答え3

質問の一部は次のとおりです。

IFS=$'\n' a=($str)

左から右に評価される 2 つの個別のグローバル変数割り当てとして解釈されます。

IFS=$'\n'; a=($str)

または

IFS=$'\n'
a=($str)

これは、グローバルが変更される理由と、IFSトークンが新しい値を持つ配列要素に分割される理由を説明します。$strIFS

IFS次のように変更効果を制限するためにサブシェルを使用できます。

str="value 0:value 1"
a=( old values )
( # Following code runs in a subshell
 IFS=":"
 a=($str)
 printf 'Subshell IFS: %q\n' "${IFS}"
 echo "Subshell: a[0]='${a[0]}' a[1]='${a[1]}'"
)
printf 'Parent IFS: %q\n' "${IFS}"
echo "Parent: a[0]='${a[0]}' a[1]='${a[1]}'"

aしかし、すぐに修正がサブシェルに限定されていることがわかります。

Subshell IFS: :
Subshell: a[0]='value 0' a[1]='value 1'
Parent IFS: $' \t\n'
Parent: a[0]='old' a[1]='values'

次に、次のソリューションを使用してIFSを保存/復元できます。この以前の回答local IFS@mswを使用するか、関数の内部を使用してみてください。提案に従ってください@helpermethodへ。しかし、すぐにあらゆる種類の問題に直面するでしょう。特に、スクリプト呼び出しの誤った動作に強力でなければならないライブラリの作成者であれば、そうです。

  • 最初に設定されていない場合はIFSどうなりますか?
  • set -u(別名)を使って実行するとset -o nounsetどうなりますか?
  • 読み取り専用に設定するとIFSどうなりますかdeclare -r IFS
  • trap再帰および/または非同期実行(ハンドラなど)を処理するために保存/復元メカニズムが必要な場合はどうすればよいですか。

IFSを保存/復元しないでください。代わりに一時修正に固執してください。

  • 変数の変更を単一のコマンド、組み込み、または関数呼び出しに制限するにはIFS="value" command

    • 複数の変数を特定の文字に分割して読み取るには(:以下の例のように)、次のようにします。

        IFS=":" read -r var1 var2 <<< "$str"
      
    • 配列を読むにはarray_var=( $str )

        IFS=":" read -r -a array_var <<< "$str"
      
  • 変数変更の効果をサブシェルに制限します。

    • コンマで区切られた配列要素を出力するには、次のようにします。

        (IFS=","; echo "${array[*]}")
      
    • 文字列としてキャプチャするには:

        csv="$(IFS=","; echo "${array[*]}")"
      

答え4

このコマンドの場合:

IFS=$'\n' a=($str)

別の回避策があります。最初の割り当て(IFS=$'\n')に実行するコマンド(関数)を指定します。

$ split(){ a=( $str ); }
$ IFS=$'\n' split

これにより、分割が呼び出される環境にIFSが配置されますが、現在の環境には残りません。

これは常に危険なevalの使用を防ぎます。

関連情報