POSIX標準以下の順序で行われる課しワード拡張(鉱山で強調):
チルダ拡張(チルダ拡張を参照)、パラメータ拡張(パラメータ拡張を参照)、コマンド置換(コマンド置換を参照)、および算術拡張(算術拡張を参照)が最初から最後まで実行されます。トークン認識の項目5を参照してください。
IFSがnullでない限り、フィールド分割(「フィールド分割」を参照)は、手順1で生成されたフィールドの部分に対して実行されます。
set -fが有効になっていない限り、パス名展開(パス名展開を参照)が実行されます。
引用の削除(引用の削除を参照)は常に最後に実行する必要があります。
ここで私たちが興味を持つ唯一の点は最初の点です。ご覧のとおり、チルダ展開はパラメータ展開の前に処理されます。
- シェルはでチルド拡張を試みますが、チルド
echo $x
が見つからないため、続行します。
- シェルは、上のパラメーターの拡張を試み
echo $x
、$x
発見し、拡大し、コマンドラインとなりますecho ~/someDirectory
。
- 処理は続行されますが、チルダ拡張はすでに処理されているため、
~
文字はそのまま残ります。
を割り当てるときに引用符を使用$x
すると、ティルダを展開せずに通常の文字のように扱うことを明示的に要求していました。よく見落とされているのは、シェルコマンドでは文字列全体を引用符で囲む必要がないため、変数の割り当て中に展開を正しく実行できることです。
user@host:~$ set -o xtrace
user@host:~$ x=~/'someDirectory'
+ x=/home/user/someDirectory
user@host:~$ echo $x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
また、パラメーター展開の前echo
に実行できる限り、コマンドラインで展開を実行することもできます。
user@host:~$ x='someDirectory'
+ x=someDirectory
user@host:~$ echo ~/$x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
なんらかの理由で$x
、展開せずにチルダを変数に影響を与えて、echo
コマンドで展開できるようにする必要がある$x
場合は、変数の2つの展開を強制的に実行するために、2回続行する必要があります。
user@host:~$ x='~/someDirectory'
+ x='~/someDirectory'
user@host:~$ echo "$( eval echo $x )"
++ eval echo '~/someDirectory'
+++ echo /home/user/someDirectory
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
ただし、そのような構造を使用するコンテキストによっては、望ましくない副作用が発生する可能性があることに注意してください。経験則として、eval
別の方法があるときに必要なものは使用しないことをお勧めします。
他の種類の拡張とは対照的にティルダの問題に具体的に対処したい場合、そのような構造はより安全で移植性があります。
user@host:~$ x='~/someDirectory'
+ x='~/someDirectory'
user@host:~$ case "$x" in "~/"*)
> x="${HOME}/${x#"~/"}"
> esac
+ case "$x" in
+ x=/home/user/someDirectory
user@host:~$ echo $x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
この構造は、先頭の存在を明示的にチェックし、~
見つかった場合はユーザーのホームディレクトリに置き換えます。
あなたのコメントに続いてx="${HOME}/${x#"~/"}"
、シェルプログラミングで使用されていない人にとっては確かに驚くかもしれませんが、実際には上記で引用した同じPOSIXルールにリンクされています。
POSIX標準によって課されるように、引用の削除は最後に行われ、パラメーターの展開は非常に早く行われます。したがって、${#"~"}
は外側の引用符の評価のはるか前に評価され、拡張されます。次に、パラメータ展開ルールで定義されているとおり:
ワードの値が必要な場合は(パラメーターの状態に基づいて、以下で説明します)、ワードはチルダ展開、パラメーター展開、コマンド置換、および算術展開の対象になります。
したがって、#
チルダの展開を回避するには、演算子の右側を適切に引用符で囲むかエスケープする必要があります。
つまり、別の言い方をすると、シェルインタープリターがを見るとx="${HOME}/${x#"~/"}"
、次のように見えます。
${HOME}
そして${x#"~/"}
拡張されなければなりません。
${HOME}
$HOME
変数の内容に展開されます。
${x#"~/"}
ネストされた展開をトリガーします:"~/"
解析されますが、引用符で囲まれているため、リテラル1として扱われます。ここでは単一引用符を使用しても同じ結果になります。
${x#"~/"}
式自体が拡張され、その結果、プレフィックス~/
がの値から削除されます$x
。
- 上記の結果が連結されました:の展開
${HOME}
、リテラル/
、展開${x#"~/"}
。
- 最終結果は二重引用符で囲まれ、機能的に単語の分割を防ぎます。私は(参照、これらの二重引用符が技術的に必要とされていないので、機能的に、ここで言うこことそこに例えば)が、個人的なスタイルとしてすぐに割り当てよう超えて何につれて
a=$b
、私は通常、それが明確に二重引用符を追加見つけるに。
ちなみに、case
構文を詳しく見てみると、上で説明"~/"*
したのと同じ概念に依存する構成がわかりますx=~/'someDirectory'
(ここでも、二重引用符と単純引用符は同じ意味で使用できます)。
これらのものが一目では見えないように見えるかもしれませんが、心配しないでください(たぶん2番目以降の光景でも!)。私の意見では、パラメーターの展開は、サブシェルでは、シェル言語でプログラミングするときに把握する最も複雑な概念の1つです。
私は一部の人が激しく反対することを知っていますが、シェルプログラミングについてさらに詳しく知りたい場合は、「高度なBashスクリプトガイド」を読むことをお勧めします。Bashスクリプトについて説明しているため、多くの拡張機能と標準機能を備えています。ホイッスルはPOSIXシェルスクリプティングと比較されましたが、実用的な例がたくさん書かれていることがわかりました。これを管理すれば、必要なときにPOSIX機能に制限するのは簡単です。POSIXレルムに直接入力することは、初心者にとって不要な急な学習曲線であると個人的には思っています(私のPOSIXチルドを@ m0dularの正規表現のような正規表現で置き換えると比較してください)私が何を言っているのかを理解するのと同じです;)!)
1:これにより、ダッシュが正しく実装されていないDashのバグが見つかります(を使用して検証可能x='~/foo'; echo "${x#~/}"
)。パラメータの展開は、ユーザーとシェル開発者自身の両方にとって複雑なフィールドです。
x='~'; print -l ${x} ${~x}
。bash
しばらくマニュアルを掘り下げてあきらめました。