Bash Basics | Basicsトップページ | トップページ

クォート

予約語メタ文字制御演算子はシェルでは特別な意味を持ちます。
これらの文字列を単純な文字列として扱いたい場合や、パラメータ展開を抑止するためにクォートを使用します。
クォートを行う方法は以下の3通りがあります。

  • エスケープ文字
    クォートされていないバックスラッシュ「\」に続く1文字は単純な文字として扱うようにメタ文字制御演算子の特別な意味をエスケープ(打ち消し)します。この「\」をエスケープ文字と言います。
    「\」に改行文字が続く場合は、その行が継続することを意味します。(つまり、実質的に改行文字が打ち消されます。)
  • シングルクォート
    2つのシングルクォート「'」に囲まれた文字(列)は単純な文字(列)として扱われ、予約語メタ文字制御演算子が含まれていても単純な文字として扱われます。
    シングルクォート「'」をシングルクォート「'」で囲むことはできません。エスケープ文字を付加してエスケープすることもできません。これはエスケープ文字がクォートされていないバックスラッシュ「\」でなければならないからです。
  • ダブルクォート
    ダブルクォート「"」もシングルクォート「'」と同様に囲まれた文字(列)を単純な文字(列)として扱いますが、$、`、\、*、@は例外となります。また、履歴展開が有効な場合は、!も例外となります。
    $、`、*、@はダブルクォート「"」で囲まれても特別な意味を失いません。
    \は次に続く文字が$、`、"、\、改行文字のいずれかである場合に限って特別な意味を失いません。
    ダブルクォート「"」の前に\を付加することでダブルクォート「"」でクォートすることができます。
  • $'<文字列>'
    $'<文字列>'の形式の単語は特別な扱いを受けます。
    <文字列>が展開され、文字列中にバックスラッシュ「\」でエスケープされている以下の文字列がある場合は、それらがデコードされます。
    バックスラッシュ
    エスケープシーケンス
    デコードされる文字
    \a警告(ベル)
    \bバックスペース
    \e
    \E
    制御文字のエスケープ文字(\x1b)
    \fフォームフィード
    \n改行文字
    \r復帰(Carriage Return)
    \t水平タブ
    \v垂直タブ
    \\バックスラッシュ
    \'シングルクォート
    \"ダブルクォート
    \<nnn>8進数値が<nnn>である8ビット文字
    \x<HH>16進数値が<HH>である8ビット文字
    \u<HHHH>16進数値が<HHHH>であるユニコード(ISO/IEC 10646)文字
    \U<HHHHHHHH>16進数値が<HHHHHHHH>であるユニコード(ISO/IEC 10646)文字
    \c<x>Control-xの文字
実行例:
$ echo 1 \< 2 \(true\)
1 < 2 (true)
$ echo '1 < 2 (true)'
1 < 2 (true)
$ echo "1 < 2 (true)"
1 < 2 (true)
3つの方法(エスケープ文字、シングルクォート、ダブルクォート)でメタ文字をクォートしています。
$ echo "Current Directory: $PWD"
/home/user1
$ echo 'Current Directory: $PWD'
Current Directory: $PWD
$ echo "Current Directory: \$PWD"
Current Directory: $PWD
ダブルクォート「"」で囲まれた文字列内の$は特別な意味を失わないため、変数PWDの値を出力することができますが、シングルクォート「'」で囲まれた文字列内では文字としての$になるため、文字列として$PWDが出力されます。
ダブルクォート「"」で囲まれた文字列内の\に続く文字が$のため、\は特別な意味を失わず、$をエスケープしています。
$ echo abcd$'\n'efg
abcd
efg
文字列abcdを出力したあと、改行(改行文字を出力)して、再び文字列efgを出力しています。
$ echo abcd$'\r'ef
efcd
文字列abcdを出力したあと、復帰(カーソルを行頭に移動)して、再び文字列efを出力しています。
結果として文字列abがefで上書きされて、文字列efcdが出力されます。
$ echo abcd$'\b'ef
abcef
文字列abcdを出力したあと、バックスペース(カーソルを1文字戻す)を出力して、再び文字列efを出力しています。
結果として文字列dが消されて、文字列abcefが出力されます。
$ echo abcd$'\f'efg$'\f'hijk
abcd
    efg
       hijk
文字列abcdを出力したあと、フォームフィード(行だけ送りカーソル位置はそのまま)して、再び文字列efgを出力してから、フォームフィードを行い、最後に文字列hijkを出力しています。
$ echo $'\x61\x62'
ab
16進数で\x61、\x62の値を持つ文字a、bを出力しています。
$ echo $'\u3042'
あ
16進数で\u3042の値を持つユニコード文字「あ」を出力しています。


ブレース展開

{}で囲んで任意の文字列、文字、整数を記述することで、指定されている選択肢を列挙したり、任意の2つの文字コード間の分字を列挙したり、任意の2つの整数間の数字を列挙することができます。
ブレース展開は以下の3つの部分で構成されます。

  • 前置部分
    省略可能で指定された場合、{}で囲まれた文字列を展開したそれぞれの結果の前に付加されます。
  • {}で囲まれたカンマ区切りの文字列または、シーケンス式
    カンマ区切りで文字列を指定する場合はクォートすることで空白文字を含むことができます。
    シーケンス式は {文字1または数値1..文字2または数値2[..増減値]} の形式で記述します。
    文字1から文字2の辞書順に並べた文字または、整数1から整数2までの数値を増減値ずつ増減したものを展開します。
    増減値は省略可能で省略した場合は1または、-1の適切な方(昇順の並びの場合は1、降順の並びの場合は-1)となります。
    整数1と整数2のどちらかの始めに0を指定(例えば、00、01、0100)すると桁数を揃えることができます。
  • 後置部分
    省略可能で指定された場合、{}で囲まれた文字列を展開したそれぞれの結果の後ろに付加されます。
  • ブレース展開は入れ子にすることもできます。

    実行例:
    $ echo {/usr,/usr/local}{/bin,/lib}
    /usr/bin /usr/lib /usr/local/bin /usr/local/lib
    $ echo 今日の{天気は{晴れ,曇り,雨},気温は{高い,低い}}
    今日の天気は晴れ 今日の天気は曇り 今日の天気は雨 今日の気温は高い 今日の気温は低い
    $ echo 今日の{"天気は "{晴れ,曇り,雨},"気温は "{高い,低い}}
    今日の天気は 晴れ 今日の天気は 曇り 今日の天気は 雨 今日の気温は 高い 今日の気温は 低い
    $ mv file.{txt,doc}
    
    {}に囲まれた文字列が順に出力されています。
    {}が連続する場合、それぞれの{}内の文字列ごとに後続の{}内の文字列が出力されます。
    日本語を記述することもできます。
    ""でクォートすることで空白文字も出力できます。
    最後の例はブレース展開を使った拡張子を変更する例です。

    $ echo {0..50}
    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 
    30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
    $ echo {00..50}
    00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
     27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
    $ echo {0..050}
    000 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 
    020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 
    040 041 042 043 044 045 046 047 048 049 050
    $ echo {50..10}
    50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24
     23 22 21 20 19 18 17 16 15 14 13 12 11 10
    $ echo {00..50..3}
    00 03 06 09 12 15 18 21 24 27 30 33 36 39 42 45 48
    $ echo {a..z}
    a b c d e f g h i j k l m n o p q r s t u v w x y z
    $ echo {a..z..2}
    a c e g i k m o q s u w y
    $ echo {Z..A}
    Z Y X W V U T S R Q P O N M L K J I H G F E D C B A
    
    {}に整数を指定することで連続する数字を出力することができます。
    0を付加することで桁揃えを行うことができます。
    大きい整数と小さい整数を指定すると数字を減少させることもできます。 増減値を指定することで一定数ずつ増減させることができます。
    文字を指定することで辞書順に文字を出力することができます。
    文字を指定した場合でも増減値を指定したり、逆順に出力することができます。

    $ echo {0..z}
    {0..z}
    $ echo {あ..お}
    {あ..お}
    $ echo {A..z}
    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [  ] ^ _ ` a b c d e f g h i
     j k l m n o p q r s t u v w x y z
    $ echo {]..z}
    {]..z}
    $ echo { ..z}
    { ..z}
    
    正しくない記述は指定した文字列がそのまま出力されます。
    日本語は指定できません。
    英大文字と英小文字の間にある記号は出力できますが、記号をブレース展開で文字として指定することはできません。


    チルダ展開

    コマンドの引き数や変数の値の代入時など)シェルが扱う文字列がクォートされていないチルダ「~」で単語が始まった場合、クォートされていないスラッシュ「/」が現れるまで(スラッシュがなければ全て)の後続の文字列は「チルダプレフィックス」として解釈されます。
    チルダプレフィックスは下記の評価と展開が行われます。

    • クォートされている文字がなく、後続の文字列が空文字列でない場合
      ログイン名として評価し、有効なログイン名である場合、そのユーザーのホームディレクトリに置き換えられます。
      有効なログイン名でない場合、置換は行われません。
    • 後続の文字列が空文字列(つまり、~、~/)の場合
      シェル変数HOMEが設定されている場合、その値に置き換えられます。
      シェル変数HONEが設定されていない場合、シェルを実行しているユーザーのホームディレクトリに置き換えられます。
    • ~+の場合
      シェル変数PWDの値に置き換えられます。
    • ~-の場合
      シェル変数OLDPWDの値に置き換えられます。
    • ~整数(数字の前に+、-の符号がある場合も含む)の場合
      ディレクトリスタック内の対応する要素に置き換えられます。
    実行例:
    $ echo ~user1
    /home/user1
    $ echo ~
    /home/user1
    $ echo ~/
    /home/user1/
    $ pwd
    /home/user1/www/images
    $ echo $PWD
    /home/user1/www/images
    $ echo ~+
    /home/user1/www/images
    $ echo $OLDPWD
    /home/user1
    $ echo ~-
    /home/user1
    $ echo ~2
    /home/user1/www/js
    $ echo ~-1
    /home/user1/www/js
    


    パラメータ展開

    文字列中に$が含まれていると、パラメータ展開、コマンド置換算術式展開が行われます。展開されるパラメータ名、変数名やシンボルはブレース{}で囲むこともできますが、ブレース{}を省略することもできます。
    ブレース{}は以下の場合に必要となります。

    • 2桁以上の数字を持つ位置パラメータを表す場合
      説明
      ${12} 12番目の位置パラメータを表します。
      $12 1番目の位置パラメータと文字2を表します。
    • 直後の文字列を変数名の一部として解釈させたくない場合
      説明
      ${fist}name 変数firstと文字列nameを表します。
      $firstname 変数firstnameを表します。
    ブレース{}を使用する場合、対になる}は文字列中に最初に現れる}となりますが、クォートされている}は単純な文字として扱われます。
    また、パラメータ変数の展開、算術式展開コマンド置換で展開される}も対とは見做されません。
    パラメータを値に展開することをパラメータ展開と呼びます。

    変数の値、長さや配列変数、および連想配列変数の要素数を展開したい場合、以下のように記述します。
    基本的な変数 配列変数 連想配列変数

    配列は各要素の値
    $変数名
    または
    ${変数名}
    ${変数名[キー]} ${変数名[キー]}
    値の長さ
    配列は各要素の長さ
    ${#変数名} ${#変数名[キー]} ${#変数名[キー]}
    配列の要素数 ${#変数名[*]}
    または
    ${#変数名[@]}
    ${#変数名[*]}
    または
    ${#変数名[@]}
    配列の全ての要素の値 * ${変数名[*]}
    または
    ${変数名[@]}
    ${変数名[*]}
    または
    ${変数名[@]}
    配列の全てのキー値
    配列の全てのキー値 *
    ${!変数名[*]}
    または
    ${!変数名[@]}
    ${!変数名[*]}
    または
    ${!変数名[@]}
    注意事項:
    配列の全ての要素、およびキー値を展開する時、ダブルクォートクォートされていた場合、以下のような違いがあります。それ以外の場合には違いはありません。
    ${変数名[*]}各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
    IFSが設定されていない場合には、空白文字で区切られます。
    IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。
    ${変数名[@]}各要素の値が別々の単語として展開されます。
    ${!変数名[*]}各キーの値がIFSの最初の文字で区切られた1つの単語として展開されます。
    IFSが設定されていない場合には、空白文字で区切られます。
    IFSが空文字列の場合には、展開される全てのキーは連結された一連の文字列となります。
    ${!変数名[@]}各キーの値が別々の単語として展開されます。

    最も単純なパラメータ変数の展開の記述形式は以下の通りです。

    変数の展開を行うときに値を変数の名前と見做して間接的に展開(変数間接展開)したり、未定義や空文字列(NULL)の場合にデフォルト値を使用(デフォルト値の展開)したり、値の一部を展開(部分文字列展開)したり、置換して展開(置換)することができます。
    1. 変数間接展開
      ${!変数名}の形で変数が参照されると、変数名の変数の値を展開すべき変数の名前と見做してさらに展開します。

      実行例:
      $ str1="Hello World"
      $ str2="Welcome to the World"
      $ var=str1
      $ echo ${!var}
      Hello World
      $ var=str2
      $ echo ${!var}
      Welcome to the World
      $ v=str
      $ i=3
      $ str3=str1
      $ eval echo \${\!${v}${i}}
      Hello World
      
    2. デフォルト値の展開
      変数の展開を行う時、未定義の変数を誤って参照した場合に意図しないエラーが発生することがあります。
      このようなエラーを抑止したり、未定義の変数変数の値が空文字列(NULL)の場合にデフォルト値を返したり、デフォルト値を代入することができます。
      以下の表に参照(展開)する変数名をVAR、デフォルト値をvalueとした場合の代入や展開結果の値を示します。
      参照方法変数 VARの値
      未定義定義済み(空文字列)定義済み(空文字列以外)
      VARへの代入展開結果
      返される値
      VARへの代入展開結果
      返される値
      VARへの代入展開結果
      返される値
      ${VAR=value}有りvalue無し空文字列無し${VAR}
      VARが未定義の場合に限り、VARへvalueが代入され、値としてvalueを返します。
      VARが空文字列を含み、既に定義されている場合、VARへの代入は行われず、VARの値を返します。
      ${VAR:=value}有りvalue有りvalue無し${VAR}
      VARが未定義、または空文字列の場合、VARへvalueが代入され、値としてvalueを返します。
      VARが空文字列を除き、既に定義されている場合、VARへの代入は行われず、VARの値を返します。
      ${VAR-value}無しvalue無し空文字列無し${VAR}
      VARが未定義の場合に限り、値としてvalueを返します。
      VARが空文字列を含み、既に定義されている場合、VARの値を返します。
      VARへの代入は行われません。
      ${VAR:-value}無しvalue無しvalue無し${VAR}
      VARが未定義、または空文字列の場合、値としてvalueを返します。
      VARが空文字列を除き、既に定義されている場合、VARの値を返します。
      VARへの代入は行われません。
      ${VAR+value}無し空文字列無しvalue無しvalue
      VARが未定義の場合、空文字列を返します。
      VARが空文字列の場合を含み、既に定義されている場合、値としてvalueを返します。
      VARへの代入は行われません。
      ${VAR:+value}無し空文字列無し空文字列無しvalue
      VARが未定義、または空文字列の場合、値として空文字列を返します。
      VARが空文字列を除き、既に定義されている場合に限って、値としてvalueを返します。
      VARに値は代入は行われません。
      ${VAR?value}エラー無し空文字列無し${VAR}
      VARが未定義の場合、標準エラー出力に"シェル名: VAR: value"を出力してエラーを発生させます。
      VARが空文字列を含み、既に定義されている場合、値としてVARの値を返します。
      VARへの代入は行われません。
      ${VAR:?value}エラーエラー無し${VAR}
      VARが未定義、または空文字列の場合、標準エラー出力に"シェル名: VAR: value"を出力してエラーを発生させます。
      VARが空文字列を除き、既に定義されている場合に限って、値としてVARの値を返します。
      VARへの代入は行われません。
    3. 部分文字列展開
      変数の展開を行う時、任意の位置から任意の文字数だけを取り出したいということがあります。
      以下の表に参照(展開)する変数名をVARとした場合の任意の位置の文字から任意の文字数を展開する方法について示します。
      参照方法展開結果例、備考
      ${VAR:offset:length} VARの値のoffsetの位置(先頭の文字を0とし1文字ずつ数える)の文字からlengthの長さの文字列が返されます。
      offsetとlengthは算術式が記述できます。
      offsetが負の値の場合、VARの値の末尾からのオフセットと見做されます。
      lengthが負の値の場合、VARの値の末尾からのオフセットと見做され、長さではなく、offsetの位置の文字からlengthの位置の文字までの文字列が返されます。
      $ str1="Hello World"
      $ echo ${str1:6:5}
      World
      $ echo ${str1:6:10}
      World
      $ echo ${str1:1:5}
      ello
      $ echo ${str1:20:5}
      
      $ echo ${str1:((-5)):3}
      Wor
      $ echo ${str1:((-5)):((-1))}
      Worl
      $ i=-8; j=-3; echo ${str1:i:j}
      lo Wo
      $ str2="ようこそこんにちは"
      $ echo ${str2:4:5}
      こんにちは
      $ echo ${str2:0:4}
      ようこそ
      $ echo ${str2:((-5)):((-1))}
      こんにち
      $ i=-8; j=-3; echo ${str2:i:j}
      うこそこん
      
      ${VAR:offset} VARの値のoffsetの位置(先頭の文字を0とし1文字ずつ数える)の文字から末尾までの文字列が返されます。
      offsetは算術式が記述できます。
      offsetが負の値の場合、VARの値の末尾からのオフセットと見做されます。
      $ str1="Hello World"
      $ echo ${str1:3}
      lo World
      $ echo ${str1:((-8))}
      lo World
      $ str2="ようこそこんにちは"
      $ i=-4; echo ${str2:4}
      こんにちは
      $ i=-4; echo ${str2:i}
      んにちは
      
      ${VAR[*]:offset:length} VARが配列変数で、キーに*が指定された場合、がoffsetの位置からlength個の配列変数の要素が返されます。
      offsetが負の値の場合、最後の要素からのオフセットと見做されます。
      lengthに負の値を指定することはできません。

      ダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。
      $ arr=(apple orange melon banana grape)
      $ echo ${arr[*]:2:2}
      melon banana
      $ echo ${arr[*]:-3:2}
      apple orange melon banana grape
      $ IFS=""; echo "${arr[*]:2:2}"
      melonbanana
      $ IFS=","; echo "${arr[*]:2:2}"
      melon,banana
      
      ${VAR[*]:offset} VARが配列変数で、キーに*が指定された場合、offsetの位置から最後の要素までが返されます。
      offsetが負の値の場合、最後の要素からのオフセットと見做されます。

      ダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。
      ${VAR[@]:offset:length} VARが配列変数で、キーに@が指定された場合、がoffsetの位置からlength個の配列変数の要素が返されます。
      offsetが負の値の場合、最後の要素からのオフセットと見做されます。
      lengthに負の値を指定することはできません。
      $ arr=(apple orange melon banana grape)
      $ echo ${arr[@]:2:2}
      melon banana
      $ echo ${arr[@]:-3:2}
      apple orange melon banana grape
      
      ${VAR[@]:offset} VARが配列変数で、キーに@が指定された場合、offsetの位置から最後の要素までが返されます。
      offsetが負の値の場合、最後の要素からのオフセットと見做されます。
      ${*:offset:length} 変数ではなく、*が指定された場合、offsetの位置からlength個の位置パラメータが返されます。
      offsetが負の値の場合、最後の位置パラメータからのオフセットと見做されます。
      offsetが0の場合、$0を始めとする位置パラメータが対象となります。
      lengthに負の値を指定することはできません。

      ダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。
      例として下記のようなスクリプトoffset.shを作成します。
      #!/usr/bin/bash
      
      while getopts ":l:o:I:" OPT
      do
          case "$OPT" in
          "l" ) length="$OPTARG";;
          "o" ) offset="$OPTARG";;
          "I" ) IFS="$OPTARG";;
      esac
      done
      
      shift $(($OPTIND - 1))
      
      if [[ -z ${length} ]]; then
          echo "${*:offset}"
      else
          echo "${*:offset:length}"
      fi
      

      実行例:
      $ ./offset.sh -o3 -l1 apple orange melon banana
      melon
      $ ./offset.sh -o-2 -l3 apple orange melon banana
      melon banana
      $ ./offset.sh -o0 -l3 apple orange melon banana
      ./offset.sh apple orange
      $ ./offset.sh -o3  apple orange melon banana
      melon banana
      $ ./offset.sh -o0  apple orange melon banana
      ./offset.sh apple orange melon banana
      $ ./offset.sh -o2 -l3 -I'' apple orange melon banana
      melonabanana
      $ ./offset.sh -o2 -l3 -I, apple orange melon banana
      orange,melon,banana
      
      ${*:offset} 変数ではなく、*が指定された場合、offsetの位置から最後までの位置パラメータが返されます。
      offsetが負の値の場合、最後の位置パラメータからのオフセットと見做されます。
      offsetが0の場合、$0から全ての位置パラメータが返されます。

      ダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定 されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素 は連結された一連の文字列となります。
      ${@:offset:length} 変数ではなく、@が指定された場合、offsetの位置からlength個の位置パラメータが返されます。
      offsetが負の値の場合、最後の位置パラメータからのオフセットと見做されます。
      offsetが0の場合、$0を始めとする位置パラメータが対象となります。
      lengthに負の値を指定することはできません。
      例として下記のようなスクリプトoffset.shを作成します。
      #!/usr/bin/bash
      
      while getopts ":l:o:" OPT
      do
          case "$OPT" in
          "l" ) length="$OPTARG";;
          "o" ) offset="$OPTARG";;
      esac
      done
      
      shift $(($OPTIND - 1))
      
      if [[ -z ${length} ]]; then
          echo ${@:offset}
      else
          echo ${@:offset:length}
      fi
      

      実行例:
      $ ./offset.sh -o3 -l1 apple orange melon banana
      melon
      $ ./offset.sh -o-2 -l3 apple orange melon banana
      melon banana
      $ ./offset.sh -o0 -l3 apple orange melon banana
      ./offset.sh apple orange
      $ ./offset.sh -o3  apple orange melon banana
      melon banana
      $ ./offset.sh -o0  apple orange melon banana
      ./offset.sh apple orange melon banana
      
      ${@:offset} 変数ではなく、@が指定された場合、offsetの位置から最後までの位置パラメータが返されます。
      offsetが負の値の場合、最後の位置パラメータからのオフセットと見做されます。
      offsetが0の場合、$0から全ての位置パラメータが返されます。
      offsetとlengthに負の値をリテラルとして指定する場合、:と-の間に1文字以上の空白文字を入れて記述するか、(())の形式で算術式として記述する必要があります。
      連想配列変数に対して部分文字列展開を行った場合の結果は不定です。配列変数のように動作するかもしれませんが、それを期待してはいけません。
    4. 置換
      変数の展開を行う時、値の一部を削除したり、置換したいということがあります。
      以下の表に参照(展開)する変数名をVARとした場合の値の一部、または全てを置換して展開する方法について示します。
      置換に使用できるパターンはパス名展開パターンマッチングと同様です。
      参照方法展開結果
      ${VAR#pattern}
      および
      ${VAR##pattern}
      VARの値の先頭からpatternに一致する部分が削除されて返されます。

      ${VAR#pattern}の場合、patternは最短一致で評価されます。
      ${VAR##pattern}の場合、patternは最長一致で評価されます。

      キーが*、または@の配列変数、または連想配列変数をVARとして指定した場合、配列の全ての要素に対して置換が適用されてリストとして返されます。
      ただし、キーが*の場合でダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。

      *、または@をVARとして指定した場合、位置パラメータの全ての要素に対して置換が適用されてリストとして返されます。
      ただし、*の場合でダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。
      $ animal="cat,dog,monkey,cow,bird"
      $ echo ${animal#*,}
      dog,monkey,cow,bird
      $ echo ${animal#*dog,}
      monkey,cow,bird
      $ echo ${animal##*,}
      bird
      $ echo ${PWD#*/}
      home/user1
      $ echo ${PWD##*/}
      user1
      $ unset arr; declare -a arr
      $ arr=("2020/01/01" "2021/10/30" "2022/03/31")
      $ echo ${arr[@]#*[0-9][0-9]/}
      01/01 10/30 03/31
      $ echo ${arr[@]##*[0-9][0-9]/}
      01 30 31
      $ unset arr; declare -a arr
      $ arr+=("cat,dog,bird")
      $ arr+=("orange,apple,lemon")
      $ arr+=("red,bule,yellow")
      $ echo ${arr[@]#*,}
      dog,bird apple,lemon bule,yellow
      $ IFS=; echo "${arr[*]#*,}"
      dog,birdapple,lemonbule,yellow
      $ IFS=/; echo "${arr[*]#*,}"
      dog,bird/apple,lemon/bule,yellow
      $ unset arr; declare -A arr
      $ arr+=([animal]="cat,dog,bird")
      $ arr+=([fruit]="orange,apple,lemon")
      $ arr+=([color]="red,bule,yellow")
      $ IFS=/; echo "${arr[*]#*,}"
      dog,bird/apple,lemon/bule,yellow
      

      例として以下のようなremovepattern.shを作成します。
      #!/usr/bin/bash
      
      while getopts ":lp:I:" OPT
      do
          case "$OPT" in
          "l" ) longest="TRUE";;
          "p" ) pattern="$OPTARG";;
          "I" ) IFS="$OPTARG";;
          esac
      done
      
      shift $(($OPTIND - 1))
      
      if [[ -z ${longest} ]]; then
          echo "${*#$pattern}"
      else
          echo "${*##$pattern}"
      fi
      

      実行例:
      $ unset arr; declare -A arr
      $ arr+=([animal]="cat,dog,bird")
      $ arr+=([fruit]="orange,apple,lemon")
      $ arr+=([color]="red,bule,yellow")
      $ ./removepattern.sh -p'*,' ${arr[@]}
      dog,bird apple,lemon bule,yellow
      $ ./removepattern.sh -p'*,' -l ${arr[@]}
      bird lemon yellow
      $ ./removepattern.sh -p'*,' -I, -l ${arr[@]}
      bird,lemon,yellow
      $ ./removepattern.sh -p'*,' -I '' -l ${arr[@]}
      birdlemonyellow
      
      ${VAR%pattern}
      および
      ${VAR%%pattern}
      VARの値の後ろからpatternに一致する部分が削除されて返されます。

      ${VAR%pattern}の場合、patternは最短一致で評価されます。
      ${VAR%%pattern}の場合、patternは最長一致で評価されます。

      キーが*、または@の配列変数、または連想配列変数をVARとして指定した場合、配列の全ての要素に対して置換が適用されてリストとして返されます。
      ただし、キーが*の場合でダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。

      *、または@をVARとして指定した場合、位置パラメータの全ての要素に対して置換が適用されてリストとして返されます。
      ただし、*の場合でダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。
      $ animal="cat,dog,monkey,cow,bird"
      $ echo ${animal%,*}
      cat,dog,monkey,cow
      $ echo ${animal%,cow*}
      cat,dog,monkey
      $ echo ${animal%%,*}
      cat
      $ echo ${PWD%/*}
      /home
      $ unset arr; declare -a arr
      $ arr=("2020/01/01" "2021/10/30" "2022/03/31")
      $ echo ${arr[@]%*/[0-9][0-9]}
      2020/01 2021/10 2022/03
      $ unset arr; declare -a arr
      $ arr+=("cat,dog,bird")
      $ arr+=("orange,apple,lemon")
      $ arr+=("red,bule,yellow")
      $ echo ${arr[@]%,*}
      cat,dog orange,apple red,bule
      $ IFS=; echo "${arr[*]%,*}"
      cat,dogorange,applered,bule
      $ IFS=/; echo "${arr[*]%,*}"
      cat,dog/orange,apple/red,bule
      $ unset arr; declare -A arr
      $ arr+=([animal]="cat,dog,bird")
      $ arr+=([fruit]="orange,apple,lemon")
      $ arr+=([color]="red,bule,yellow")
      $ IFS=/; echo "${arr[*]%,*}"
      cat,dog/orange,apple/red,bule
      

      例として以下のようなremovepattern.shを作成します。
      #!/usr/bin/bash
      
      while getopts ":lp:I:" OPT
      do
          case "$OPT" in
          "l" ) longest="TRUE";;
          "p" ) pattern="$OPTARG";;
          "I" ) IFS="$OPTARG";;
          esac
      done
      
      shift $(($OPTIND - 1))
      
      if [[ -z ${longest} ]]; then
          echo "${*%$pattern}"
      else
          echo "${*%%$pattern}"
      fi
      

      実行例:
      $ unset arr; declare -A arr
      $ arr+=([animal]="cat,dog,bird")
      $ arr+=([fruit]="orange,apple,lemon")
      $ arr+=([color]="red,bule,yellow")
      $ ./removepattern.sh -p',*' ${arr[@]}
      cat,dog orange,apple red,bule
      $ ./removepattern.sh -p',*' -l ${arr[@]}
      cat orange red
      $ ./removepattern.sh -p',*' -I, -l ${arr[@]}
      cat,orange,red
      $ ./removepattern.sh -p',*' -I '' -l ${arr[@]}
      catorangered
      
      ${VAR/pattern/string}
      および
      ${VAR//pattern/string}
      VARの値のpatternに最長一致する部分がstringに置換されて返されます。

      ${VAR/pattern/string}の場合、最初に一致した部分だけが置換されます。
      ${VAR//pattern/string}の場合、一致した全ての部分が置換されます。

      patternが#で始まる場合、VARの先頭と見做されます。(正規表現の^に相当します。)
      patternが%で始まる場合、VARの末尾と見做されます。(つまりpatternが"%abc"の場合、VARがabcで終ると一致します。)

      キーが*、または@の配列変数、または連想配列変数をVARとして指定した場合、配列の全ての要素に対して置換が適用されてリストとして返されます。
      ただし、キーが*の場合でダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。

      *、または@をVARとして指定した場合、位置パラメータの全ての要素に対して置換が適用されてリストとして返されます。
      ただし、*の場合でダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。
      $ animal="cat, dog, monkey, cow, bird"
      $ echo ${animal/monkey/bear}
      cat, dog, bear, cow, bird
      $ echo ${animal/, /,}
      cat,dog, monkey, cow, bird
      $ echo ${animal//, /,}
      cat,dog,monkey,cow,bird
      $ echo ${animal/*,/animal:}
      animal: bird
      $ echo ${PWD//\//\\}
      \home\user1
      $ echo -e ${PATH//:/'\n'}
      /usr/local/bin
      /bin
      /usr/bin
      /usr/local/sbin
      /usr/sbin
      /home/user1/.local/bin
      /home/user1/bin
      $ unset arr; declare -a arr
      $ arr+=("cat,dog,bird")
      $ arr+=("orange,apple,lemon")
      $ arr+=("red,bule,yellow")
      $ echo ${arr[@]/,/;}
      cat;dog,bird orange;apple,lemon red;bule,yellow
      $ echo ${arr[@]//,/;}
      cat;dog;bird orange;apple;lemon red;bule;yellow
      $ IFS=";"; echo "${arr[*]//,/;}"
      cat;dog;bird;orange;apple;lemon;red;bule;yellow
      $ ./replace.sh -p '#' -s "@"  ${arr[@]}
      @cat,dog,bird @orange,apple,lemon @red,bule,yellow
      $ ./replace.sh -p '#[or]' -s "@"  ${arr[@]}
      cat,dog,bird @range,apple,lemon @ed,bule,yellow
      $ ./replace.sh -p '%' -s "@"  ${arr[@]}
      cat,dog,bird@ orange,apple,lemon@ red,bule,yellow@
      $ ./replace.sh -p '%w' -s '@'  ${arr[@]}
      cat,dog,bird orange,apple,lemon red,bule,yello@
      

      例として以下のようなreplace.shを作成します。
      #!/usr/bin/bash
      
      while getopts ":p:rs:I:" OPT
      do
          case "$OPT" in
          "p" ) pattern="$OPTARG";;
          "r" ) repeat="TRUE";;
          "s" ) string="$OPTARG";;
          "I" ) IFS="$OPTARG";;
          esac
      done
      
      shift $(($OPTIND - 1))
      
      if [[ -z ${repeat} ]]; then
          echo "${*/${pattern}/${string}}"
      else
          echo "${*//${pattern}/${string}}"
      fi
      

      実行例:
      $ unset arr; declare -A arr
      $ arr+=([animal]="cat,dog,bird")
      $ arr+=([fruit]="orange,apple,lemon")
      $ arr+=([color]="red,bule,yellow")
      $ ./replace.sh -r -p, -s";" ${arr[@]}
      cat;dog;bird orange;apple;lemon red;bule;yellow
      $ ./replace.sh -p, -s";" ${arr[@]}
      cat;dog,bird orange;apple,lemon red;bule,yellow
      $ ./replace.sh -r -p, -s";" -I '' ${arr[@]}
      cat;dog;birdorange;apple;lemonred;bule;yellow
      $ ./replace.sh -r -p"[!a-c,]" -s"x" ${arr[@]}
      cax,xxx,bxxx xxaxxx,axxxx,xxxxx xxx,bxxx,xxxxxx
      
      ${VAR/pattern/},
      ${VAR/pattern}
      および
      ${VAR//pattern/},
      ${VAR//pattern}
      VARの値のpatternに最長一致する部分が削除されて返されます。

      ${VAR/pattern/}、または${VAR/pattern}の場合、最初に一致した部分だけが削除されます。
      ${VAR//pattern/}、または${VAR//pattern}の場合、一致した全ての部分が削除されます。

      patternが#で始まる場合、VARの先頭と見做されます。(正規表現の^に相当します。)
      patternが%で始まる場合、VARの末尾と見做されます。(つまりpatternが"%abc"の場合、VARがabcで終ると一致します。)

      キーが*、または@の配列変数、または連想配列変数をVARとして指定した場合、配列の全ての要素に対して削除が適用されてリストとして返されます。
      ただし、キーが*の場合でダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。

      *、または@をVARとして指定した場合、位置パラメータの全ての要素に対して削除が適用されてリストとして返されます。
      ただし、*の場合でダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。
      $ animal="cat, dog, monkey, cow, bird"
      $ echo ${animal/monkey/}
      cat, dog, , cow, bird
      $ echo ${animal/, /}
      catdog, monkey, cow, bird
      $ echo ${animal//, /}
      catdogmonkeycowbird
      $ echo ${animal/*,/}
      bird
      $ echo ${PWD//\//}
      homeuser1
      $ unset arr; declare -a arr
      $ arr+=("cat,dog,bird")
      $ arr+=("orange,apple,lemon")
      $ arr+=("red,bule,yellow")
      $ echo ${arr[@]/,}
      catdog,bird orangeapple,lemon redbule,yellow
      $ echo ${arr[@]//,}
      catdogbird orangeapplelemon redbuleyellow
      $ IFS=";"; echo "${arr[*]//,}"
      catdogbird;orangeapplelemon;redbuleyellow
      $ shopt -s extglob
      $ echo ${arr[@]//@([a-e][a-e])}
      t,dog,bird orange,apple,lemon r,bule,yellow
      

      例として以下のようなremovepattern.shを作成します。
      #!/usr/bin/bash
      
      while getopts ":p:rI:" OPT
      do
          case "$OPT" in
          "p" ) pattern="$OPTARG";;
          "r" ) repeat="TRUE";;
          "I" ) IFS="$OPTARG";;
          esac
      done
      
      shift $(($OPTIND - 1))
      
      if [[ -z ${repeat} ]]; then
          echo "${*/${pattern}/}"
      else
          echo "${*//${pattern}/}"
      fi
      

      実行例:
      $ unset arr; declare -A arr
      $ arr+=([animal]="cat,dog,bird")
      $ arr+=([fruit]="orange,apple,lemon")
      $ arr+=([color]="red,bule,yellow")
      $ ./removepattern.sh -r -p, -I '' ${arr[@]}
      catdogbirdorangeapplelemonredbuleyellow
      $ ./removepattern.sh -r -p"[!a-c,]" ${arr[@]}
      ca,,b a,a, ,b, ,,a,
      
      ${VAR^pattern}
      および
      ${VAR^^pattern}
      VARの値のpatternに一致する文字(文字列ではないことに注意してください)が英大文字に置換されて返されます。patternは一文字と一致するように指定する必要があります。(例えば、[a-z][a-z]は2文字となるため、文字とは一致しません。)
      patternを省略した場合、?を指定したものと見做され、全ての英小文字が英大文字に置換されます。

      ${VAR^pattern}の場合、最初に一致した文字だけが置換されます。
      ${VAR^^pattern}の場合、一致した全ての文字が英大文字に置換されます。

      キーが*、または@の配列変数、または連想配列変数をVARとして指定した場合、配列の全ての要素に対して置換が適用されてリストとして返されます。
      ただし、キーが*の場合でダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。

      *、または@をVARとして指定した場合、位置パラメータの全ての要素に対して置換が適用されてリストとして返されます。
      ただし、*の場合でダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。
      $ animal="cat, dog, monkey, cow, bird"
      $ echo ${animal^[a-c]}
      Cat, dog, monkey, cow, bird
      $ echo ${animal^^[a-c]}
      CAt, dog, monkey, Cow, Bird
      $ echo ${animal^^}
      CAT, DOG, MONKEY, COW, BIRD
      
      ${VAR,pattern}
      および
      ${VAR,,pattern}
      VARの値のpatternに一致する文字(文字列ではないことに注意してください)が英小文字に置換されて返されます。patternは一文字と一致するように指定する必要があります。(例えば、[a-z][a-z]は2文字となるため、文字とは一致しません。)
      patternを省略した場合、?を指定したものと見做され、全ての英大文字が英小文字に置換されます。

      ${VAR^pattern}の場合、最初に一致した文字だけが置換されます。
      ${VAR^^pattern}の場合、一致した全ての文字が英小文字に置換されます。

      キーが*、または@の配列変数、または連想配列変数をVARとして指定した場合、配列の全ての要素に対して置換が適用されてリストとして返されます。
      ただし、キーが*の場合でダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。

      *、または@をVARとして指定した場合、位置パラメータの全ての要素に対して置換が適用されてリストとして返されます。
      ただし、*の場合でダブルクォートクォートされていた場合、各要素の値がIFSの最初の文字で区切られた1つの単語として展開されます。
      IFSが設定されていない場合には、空白文字で区切られます。
      IFSが空文字列の場合には、展開される全ての要素は連結された一連の文字列となります。
      $ animal="CAT, DOG, MONKEY, COW, BIRD"
      $ echo ${animal,[a-c]}
      Cat, dog, monkey, cow, bird
      $ echo ${animal,,[A-C]}
      caT, DOG, MONKEY, cOW, bIRD
      $ echo ${animal,,}
      cat, dog, monkey, cow, bird
      


    コマンド置換

    以下のいずれかの形式でコマンドを記述するとそのコマンドの実行結果の標準出力に置換されます。
    これにより、コマンドの引き数に別のコマンド標準出力を与えたり、コマンド標準出力変数に代入したりすることができます。

    $(コマンド)
    または
    `コマンド`

    bashはサブシェル内でコマンドを実行し、コマンド標準出力コマンド置換の記述部分を置き換えます。
    標準出力した内容の末尾にある改行文字は削除されますが、出力された内容の途中にある改行文字は削除されません。
    ただし、単語分割の際に削除されることがあります。
    $(コマンド)の形式の場合、()内の全ての文字がコマンド文字列として実行されます。
    `コマンド`の形式の場合、バックスラッシュ「\」が前置されていないバッククォート「`」までがコマンド文字列として実行され、コマンド文字列にバックスラッシュが含まれる場合、$、`、\の前にある場合を除いて文字としてのバックスラッシュとして扱われます。
    `コマンド`の形式は古い形式で使用は推奨されていません。

    コマンド置換は入れ子にすることができますが、`コマンド`の形式を使った入れ子を行う場合、内側のバッククォートをバックスラッシュでエスケープする必要があります。

    実行例:

    $ HTTPD_PID=$(ps --noheader -opid -C httpd); echo $HTTPD_PID
    1520 1717 7309 12312 12317 21135 21143 21176 21179 21180 22699 28100
    
    実行中のhttpdのプロセスIDを取得し変数に代入しています。
    $ TARGET_FILES=$(ls *.txt); echo $TARGET_FILES
    1.txt 11.txt 123.txt 123a.txt a.txt b.txt c.txt
    
    処理対象のファイルを取得し変数に代入しています。
    $ cmd $(date --date "-1 months" +%Y%m%d)

    コマンドcmdの引き数に一ヶ月前の日付を設定しています。
    $ DATE=($(date))
    $ for ((i=0; i<${#DATE[*]}; i++)); do echo ${DATE[$i]}; done
    2022年
    3月
    13日
    日曜日
    11:44:49
    JST

    dateコマンド標準出力配列変数"として代入する例を示しています。
    $ if [[ $(grep -q Error error.log)$? -eq 0 ]]; then echo OK; else echo NG; fi
    OK
    $ if [[ $(grep -q Error error.log) $? -eq 0 ]]; then echo OK; else echo NG; fi
    bash: 条件二項演算子が予期されます
    bash: `$?' 周辺に構文エラーがあります

    $(コマンド)$?を展開すると、コマンドは実行されますが、$(コマンド)は空文字列となり、直後の$?がそのコマンドの実行結果に展開されます。
    このため、この例にあるように条件式で実行結果を判定することができます。
    この例ではgrepコマンドで文字列がファイルに含まれているかどうかを条件式で判定しています。
    ただし、$(コマンド)$?の間を空けると構文エラーとなるので空白を空けずに続けて記述する必要があります。


    算術式展開

    以下の形式で算術式を記述するとその算術式の評価結果に置換されます。
    これにより、コマンドの引き数に算術計算の結果を与えたり、算術計算の結果を変数に代入したりすることができます。

    $(())
    式に含まれる全てのトークンに対して、パラメータ展開コマンド置換クォートの削除が行われます。

    算術式展開は入れ子にすることができます。
    算術式は算術式評価に示す規則に従って評価されます。
    式が不正な場合、エラーを出力し、置換は行われません。

    実行例:

    $ x=1; y=3; echo $((x=y+1, y=x++*2, x*y))
    40
    $ x=3; y=4; echo $((x**y))
    81
    $ x=3; y=4; echo $(( $((x++)) * ($((y++)) +x) ))
    24
    $ echo "abcdefghijklmnopqrstuvwxyz" | cut -c$((26-3))-
    wxyz
    $ eval echo \$\'\\x`printf "%x" $((97+4))`\'
    e
    
    • 変数xに1を代入、変数yに3を代入します。
      x=y+1:   変数xに変数yの値3に1を加えた結果の4を代入します。
      y=x++*2: 変数yに変数xの値4を2倍した結果の8を代入し、変数xの値をインクリメントします。
      x*y:     変数xの値5に変数yの値8を掛けた40となります。
      カンマ演算子はそれぞれの式の値4, 8, 40を左から評価し、一番右の40が出力されます。
    • 変数xに3を代入、変数yに4を代入します。
      x**y: 変数xの値3を変数yの値4で累乗した結果の81が出力されます。
    • 変数xに3を代入、変数yに4を代入します。
      変数xの値を後置インクリメントしています。
      変数xの値3(インクリメントする前の値)に変数yの値4にインクリメントされた変数xの値4を加えた8を掛けた結果、24を出力します。
    • echoで英小文字を標準出力に出力しパイプラインでcutコマンドに連結し、算術式展開の結果(26-3)23文字目以降を出力しています。
    • 算術式97+4を展開した結果の101をコマンド置換を使ってprintf "%x"で16進数表記の65とし、echo \$\'\\x65'というコマンドに展開しています。
      $、'、\は最初の展開で展開されないようにするため、バックスラッシュでクォートされています。
      生成されたコマンド文字列をevalコマンドで実行しています。結果としてASCIIコードの16進数で0x65である文字"e"が出力されています。


    プロセス置換

    システムが名前付きパイプ(FIFO)または、「/dev/fd」形式のファイルディスクリプタ指定をサポートしている場合、プロセス置換が使用できます。
    以下のいずれかの形式でリストを記述するとリスト標準入出力がファイルへの入出力と同等に展開されます。

    >(リスト)
    または
    <(リスト)

    プロセス置換を使用するとコマンドが入力としてファイルを想定している場合、ファイルの代わりにリスト標準出力から読み込むことができます。同様にファイルへの出力を想定している場合、リスト標準入力へ書き込むことができます。
    リダイレクションとは異り、コマンドの引き数にファイルを想定している(ファイル名に相当する)部分の引き数に置換することができます。
    例えば、以下のように使用することができます。

    実行例:

    $ diff <(date) <(sleep 1;date)
    1c1
    < 2022年  3月 13日 日曜日 11:43:28 JST
    ---
    > 2022年  3月 13日 日曜日 11:43:29 JST
    
    この例ではdiffコマンドの入力として2つの<()を使う例を示しています。

    $ strace -f -o >(cat >trace.log) bash
    
    この例ではstraceの出力を単純にcatで読み込んでファイルにリダイレクションしていますが、catではなく、grepなどの加工を行うコマンドをプロセス置換で使用することによってファイルに出力したものを加工するのではなく、出力時にリアルタイムに加工しながら必要なデータのみをファイルに出力するということができます。


    単語の分割

    ダブルクォートの内側以外で文字列中に$が含まれていると、パラメータ展開コマンド置換算術式展開が行われた後、展開の結果に対して単語の分割が行われます。(展開が行われなければ単語の分割も行われません。)
    以下の文字列は単語の分割は行われません。

    1. 変数へ代入する文字列。
      ただし、$@を代入する場合を除きます。
      例:
      $ WORD="Hello World"
      STR=$WORD
      
    2. ダブルクォートでクォートされている文字列。
    3. [[]]の形式の条件式。
    シェルはIFSのそれぞれの文字を区切り文字として展開の結果を単語に分割します。
    IFSが設定されていないか、デフォルト値の<空白文字><タブ文字><改行文字>と同じ値であれば、展開の結果の先頭と末尾にある<空白文字>、<タブ文字>、<改行文字>は無視され、先頭と末尾以外にある<空白文字>、<タブ文字>、<改行文字>で単語が区切られます。
    IFSがデフォルト値以外で<空白文字>、<タブ文字>を含まない場合、展開の結果の先頭にある<空白文字>、<タブ文字>は無視されません。
    IFSが空文字列の場合、単語の分割は行われません。

    下記のスクリプトtest.shを作成して確認します。
    #!/usr/bin/bash
    
    for ((i=1; i<=${#}; i++))
    do
        eval echo '\$'$i: '$'$i
    done
    

    実行例:
    $ unset IFS
    $ WORD="Hello World"
    $ ./test.sh $WORD
    $1: Hello
    $2: World
    
    IFSが未設定であるため、$WORDがパラメータ展開された後、<空白文字><タブ文字><改行文字>を区切り文字としてHelloとWorldの2つの単語としてtest.shに引き数として渡されています。
    $ unset IFS
    $ WORD="Hello World"
    $ ./test.sh "$WORD"
    $1: Hello World
    
    WORDがダブルクォートでクォートされているため、WORDがパラメータ展開された後、1つの単語としてtest.shに引き数として渡されています。
    $ unset IFS
    $ WORD='"Hello World"'
    $ ./test.sh $WORD
    $1: "Hello
    $2: World"
    
    WORDがダブルクォートで囲まれた文字列がさらにシングルクォートでクォートされているため、ダブルクォートは特別な意味を失い単純な文字として扱われます。
    WORDがパラメータ展開された後、IFSに従って2つの単語としてtest.shに渡されています。
    $ IFS='/'
    $ WORD1="Hello World"
    $ WORD2="Hello/World"
    $ WORD3="Hello///World"
    $ ./test.sh $WORD1
    $1: Hello World
    $ ./test.sh $WORD2
    $1: Hello
    $2: World
    $ ./test.sh $WORD3
    $1: Hello
    $2:
    $3:
    $4: World
    
    IFSが'/'に設定されているため、$WORD1はパラメータ展開された後、単語に区切られることなく、1つの単語としてtest.shに引き数として渡されています。
    $WORD2はIFSに設定されている区切り文字'/'で区切られてHelloとWorldの2つの単語としてtest.shに引き数として渡されています。
    $WORD3はIFSに設定されている区切り文字'/'が連続して現れるため、それぞれの区切り文字で区切られてHelloとWorldの2つの単語の間に空文字列が単語としてtest.shに引き数として渡されています。


    パス名展開

    シェルのオプションnoglobが指定されていなければ、(コマンドの引き数や変数の値の代入時など)シェルが扱う文字列の単語分割を行った後で、それぞれの単語が「*」、「?」、「[」を含んでいる場合、その単語はパターンとして解釈され、パターンマッチングの規則に従ってパターンにマッチするファイル名がアルファベット順にソートされたリストに展開されます。
    パス名のマッチングではディレクトリを区切るスラッシュ文字は明示的に指定する必要があります。

    パス名展開の動作に関連するシェルのオプションシェル変数は以下の通りです。

    オプション
    またはシェル変数
    動作
    nullglobオプション 無効: パターンにマッチするファイルが存在しない場合、指定されたパターンはそのまま文字列として残ります。
    有効: パターンにマッチするファイルが存在しない場合、指定されたパターンは削除され、空文字列が展開されます。
    failglobオプション 有効: パターンにマッチするファイルが存在しない場合、エラーメッセージが表示されコマンドの実行は失敗します。
    nocaseglobオプション 有効: パターンの評価に英大文字と英小文字の区別をしません。
    globstarオプション 有効: パス名展開でパターンに**が指定された場合、サブディレクトリを再帰的に探索し、すべてのディレクトリ、サブディレクトリとファイルにマッチします。**/が指定された場合、サブディレクトリを再帰的に探索し、すべてのディレクトリとサブディレクトリにマッチします。
    extglobオプション 有効: 拡張パターンマッチング演算子が使用できます。
    dotglobオプション 無効: .で始まるファイルを対象から削除します。
    有効: .で始まるファイルをリストに含めます。
    GLOBIGNORE変数 このシェル変数に指定されたパターンにマッチするファイルはリストから削除されます。
    パターンの例外(マッチさせたくないもの)を指定することができます。
    GLOBIGNOREに空文字列以外が指定されると、dotglobオプションも同時に有効化されるため、.で始まるファイルを除外したい場合、.*をGLOBIGNOREに含める必要があります。GLOBIGNOREunsetするとdotglobも無効化されます。

    パタンーンマッチング

    パターンマッチングは条件式コマンド履歴、パス名展開で評価されます。
    条件式では文字列がパターンとマッチするかどうかを評価します。コマンド履歴ではパターンにマッチする履歴を呼び出します。パス名展開ではパターンにマッチするファイル名をアルファベット順にソートしたリストに置き換えられます。
    パターンに含まれる以下の特殊パターン文字「*」、「?」、「[」と「]」を単なる文字として認識させたい場合、クォートする必要があります。
    パターンがクォートされている場合、パターンに含まれる特殊パターン文字はすべて単純な文字として認識されます。


    シェルのオプションextglobが有効な場合、複数のパターン(|で区切られた1つ以上のパターンのリスト)を複合的に利用することができる下記の拡張パターンマッチング演算子が使用できます。
    • ?(パターンリスト)
      指定されたパターンが0回または1回現れるとマッチします。
    • *(パターンリスト)
      指定されたパターンが0回以上現れるとマッチします。
    • +(パターンリスト)
      指定されたパターンが1回以上現れるとマッチします。
    • @(パターンリスト)
      指定されたパターンが1回のみの場合、マッチします。
    • !(パターンリスト)
      指定されたパターンのいずれでもない場合、マッチします。

    実行例:
    $ touch 1.txt  11.txt  123.txt  123a.txt  A.TXT  a.txt  aa.doc  b.txt  c.txt
    $ touch 報告書.doc お知らせ.doc
    $ ls
    1.txt   123.txt   A.TXT  aa.doc  c.txt         報告書.doc
    11.txt  123a.txt  a.txt  b.txt   お知らせ.doc
    
    ファイルをtouchで作成します。

    $ echo *
    1.txt 11.txt 123.txt 123a.txt A.TXT a.txt aa.doc b.txt c.txt お知らせ.doc 報告書.doc
    $ echo ?.txt
    1.txt a.txt b.txt c.txt
    
    もよく使う*、?をパターンとして指定した例です。

    $ echo [0-9][0-9].txt
    11.txt
    $ echo ?.txt
    1.txt a.txt b.txt c.txt
    $ echo [報告]*
    報告書.doc
    $ echo [![:digit:]]*
    A.TXT a.txt aa.doc b.txt c.txt お知らせ.doc 報告書.doc
    
    []に文字の集合をパターンとして指定した例です。
    漢字も問題なく使用できます。
    !を使って指定した文字集合以外とすることもできます。

    $ echo [[:digit:]].txt
    1.txt
    $ echo [[:alpha:]].txt
    a.txt b.txt c.txt
    $ echo [[:jhira:]]*
    お知らせ.doc
    $ echo [[:jkanji:]][[:jkanji:]][[:jkanji:]].doc
    報告書.doc
    
    []に文字クラスをパターンとして指定した例です。
    [:digit:]も[:alpha:]も1文字にマッチするため、拡張子よりも前の部分が1文字のファイルのみマッチしています。
    ロケールが日本語環境の場合には、[:jhira:]や[:jkanji:]も全角文字1文字とマッチすることがわかります。

    $ echo [[.one.]].txt
    1.txt
    
    []に照合シンボルをパターンとして指定した例です。

    $ touch '*.txt'
    $ ls
    *.txt  11.txt   123a.txt  a.txt   b.txt  お知らせ.doc
    1.txt  123.txt  A.TXT     aa.doc  c.txt  報告書.doc
    $ rm [[.asterisk.]].txt
    $ ls
    1.txt   123.txt   A.TXT  aa.doc  c.txt         報告書.doc
    11.txt  123a.txt  a.txt  b.txt   お知らせ.doc
    
    照合シンボルを使用する例はあまりないかも知れませんが、例えば、特殊文字を含むファイルを誤って作成してしまった場合などにも対応が可能です。

    $ shopt -s extglob
    $ echo @([[:digit:]]|[[:alpha:]]).txt
    1.txt a.txt b.txt c.txt
    $ echo [[:digit:][:alpha:]].txt
    1.txt a.txt b.txt c.txt
    $ echo *([[:digit:]]).txt
    1.txt 11.txt 123.txt
    $ echo *([0-9]|[ab]).txt
    1.txt 11.txt 123.txt 123a.txt a.txt b.txt
    $ echo *([0-9][0-9][0-9]|[ab]).*
    123.txt 123a.txt a.txt aa.doc b.txt
    $ shopt -s nocaseglob
    $ echo *([0-9][0-9][0-9]|[ab]).txt
    123.txt 123a.txt A.TXT a.txt aa.doc b.txt
    
    シェルのオプションによる動作の変化の例です。
    @([[:digit:]]|[[:alpha:]])は数字1文字かアルファベット1文字の1回にマッチするので、[[:digit:][:alpha:]]と同じ意味になります。
    *([0-9][0-9][0-9]|[ab])は3文字の数字かaまたはbの0回以上の繰り返しにマッチします。
    nocaseglobオプションを有効にすると英大文字と英小文字の区別がなくなります。


    クォートの削除

    先行して行われる展開が全て終わった後、クォートされていない \、'、" が展開されずに残っている(展開の結果ではない)場合、全て削除されます。

    実行例:

    $ echo \c
    c
    $ echo "Hello"
    Hello
    $ echo 'Hello'
    Hello
    
    クォートされていない \、'、" は削除されるため、全て出力されません。
    $ echo '\'
    \
    $ echo "'"
    '
    $ echo '"'
    "
    
    クォートされている \、'、" は削除されないため、全て出力されます。
    $ echo {\\,\',\"}
    \ ' "
    
    展開の結果出力される \、'、" は削除されないため、全て出力されます。