質問: Google Colaboratory の `!` 記法で変数と文字列を連結する方法を教えてください。 また、不可解な挙動もあり、文法を知りたく思っています。 Python初心者ですが、Linuxのコマンドラインは使用経験があります。 以下はGoogle Colaboratoryで試行錯誤した例です。 **実行したコード** ``` fc="test.txt" !echo $fc !echo "$fc".bak !echo ${fc}.bak !echo {fc}.bak ``` **結果** ``` test.txt test.txt.bak .txt.bak test.txt.bak ``` **質問** 1. Google colaboratoryの `!` 記法で変数と文字列を連結する正しい方法は 「3行目」と「5行目」のどちらでしょうか?またはその他でしょうか? 2. 4行目の書き方で、"test"が消える理由は何ですか? Pythonの文法で"$"の意味を調べようと しましたが、正規表現における"$"の意味しか見つかりませんでした。 $変数名を試した理由は、以下のページなどで$変数名とするとあったからです。 JupyterやColabのマジックコマンドに対してセル内で定義した変数を引数として与える - Qiita ここで自分はLinuxと同じやり方として、連結のために"{}"で囲む方法を試しました。 また最後の行で$なしも試しました。 よろしくお願いします。
回答: 変数展開 Google Colaboratory では Jupyter Notebook の IPython カーネルを使っており、変数展開も IPython に準じた挙動をします。このため、IPython のドキュメントなどを調べると良いです。 まず `!` を先頭に付ける形のシェル実行では、使える変数が 2 種類あることに注意してください。ひとつは Python の変数、もうひとつはシェルで使える変数(たとえば環境変数)です。 `$name` や `{name}` を使うと Python の変数を展開することができます。更に後者では変数に限らず一般の式を入れられます。シェルの変数を展開するには `$` を `$$` でエスケープします。 以下実行例です。 ``` # これは Python の変数 foo = 42 !echo $foo !echo {foo} !echo {foo + 1} # これは環境変数 !echo $$PATH ``` ``` 42 42 43 /usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/tools/node/bin:/tools/google-cloud-sdk/bin:/opt/bin ``` 文字列結合 Python の変数を使う場合、Python の変数が展開された後にシェルスクリプトがシェルに解釈されることに注意しつつ書けば良いです。エッジケースまで考えると面倒くさいので、Python の側で文字列を結合してしまうのが個人的には好みです。 例1 ``` fc = "test.txt" !echo '{fc + '.bak'}' ``` 例2 ``` fc = "test.txt" backup_filename = f"{fc}.bak" !echo '{backup_filename}' ``` Python の変数が展開された後にスペースやクォートなどが入ってシェルによるパースに影響しないかに注意してください。 質問文の例 最後に補足として、質問文にある 4 つの例がどのように解釈されていたかを説明します。 ``` # これは Python の変数。 fc = "test.txt" # $name 形式で Python の変数を展開しています。 !echo $fc # $name 形式で Python の変数を展開しています。シェルには "test.txt".bak という文字列が渡り、シェルでは全て文字列なのでそのまま結合されます。 !echo "$fc".bak # {name} 形式で Python の変数が展開された後、$name 形式でシェルの変数が展開されていると思われます。 # 今回の場合、まず $test.txt.bak がシェルに渡り、シェル側では $test は空文字列なので .txt.bak になります。 # このことはたとえば fc = "PATH.txt" とすると確かめられます。 !echo ${fc}.bak # {name} 形式で Python の変数を展開しています。 !echo {fc}.bak ```