bash
や zsh
などのシェルで、ディレクトリ作成と同時にディレクトリ移動をしたい場合、一般的に以下のコマンドで可能です。
mkdir my_dir && cd $_
$_
(ダラー・アンダーバー)は特殊パラメーターと呼ばれ、1つ前の処理の最後のパラメーターを保持している環境変数です(この場合 my_dir
。詳しくは後述)。
しかし、この時ディレクトリ名にスペースがあると No such file or directory
エラーが出るのです。どうしよう。
$ # スペースなしディレクトリ名で作成と同時に移動(成功)
$ mkdir my_dir && cd $_
$ # スペース付きディレクトリ名で作成と同時に移動(失敗)
$ mkdir "my dir" && cd $_
-bash: cd: my: No such file or directory
「mkdir
同時に移動
cd
」で Qiita 記事をググっても、ピンポイントに出でこなかったので、自分のググラビリティとして。
TL; DR (今北産業)
特殊パラメーター
$_
でスペース入りの値を扱うにはクォート付き("$_"
)で使う。
毎回忘れる。
mkdir "my dir" && cd "$_"
$ pwd
/Users/KEINOS/Tests/ls_dir
$ ls -la
total 0
drwxr-xr-x 2 admin staff 68 2 12 12:28 .
drwxr-xr-x 32 admin staff 1088 2 12 12:09 ..
$ mkdir "my dir1 as hoge and very long directory name"
$ mkdir "my dir2 as hoge and very long directory name"
$ ls -la
total 0
drwxr-xr-x 4 admin staff 136 2 12 12:28 .
drwxr-xr-x 32 admin staff 1088 2 12 12:09 ..
drwxr-xr-x 2 admin staff 68 2 12 12:28 my dir1 as hoge and very long directory name
drwxr-xr-x 2 admin staff 68 2 12 12:28 my dir2 as hoge and very long directory name
$ ls my*1* && cd "$_"
$ pwd
/Users/KEINOS/Tests/ls_dir/my dir1 as hoge and very long directory name
TS; DR (kwsk)
特殊環境変数_
(アンダーバー、アンダースコア)の挙動について
bash
や zsh
などのシェルには、"_
"(アンダーバー、アンダースコア)という「特殊パラメーター」(特殊環境変数)があります。
この変数には「1つ前のコマンド操作のコマンド内容をスペースに区切り、最後の区切りがパース(処理)された値」が代入されています。
また、現在の設定値を確認するには env
コマンドもしくは echo $_
で確認することができます。
これを応用するとディレクトリ作成と同時に移動などに活用できます。
$ mkdir my_very_long_directory_name && cd $_
🐒 Alpine Linux の
ash
など、特殊パラメーターが使えないシェルもあるので、Docker や CI などのスクリプトで使う場合は注意します。
スペース付きディレクトリ名に注意
シェルでスペース付きのパスを扱う場合はクォートするのが常です。この特殊パラメーター $_
(ダラー・アンダーバー)も同様に "$_"
のように引用符(ダブルクォーテーション)でクォートして使います。
クォートしないとスペース入りのディレクトリ名で No such file or directory
エラーが出るので注意します。(←俺
$ mkdir "my dir" && cd $_
-bash: cd: my: No such file or directory
$ mkdir "my dir" && cd $_
-bash: cd: my: そのようなファイルやディレクトリはありません
特殊パラメーター(スペシャルパラメーター) $_
について
_
($_
, an underscore.) At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list. Subsequently, expands to the last argument to the previous simple command executed in the foreground, after expansion. Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command. When checking mail, this parameter holds the name of the mail file.(Bash のスペシャル・パラメータ
$_
に関するリファレンス | bash | manual @ GNU.org より)
_
とは
($_
、アンダースコア、アンダーバー)シェルの起動時に、シェルを呼び出すために参照する絶対パス名、または環境設定ファイルで指定され実行されたシェル・スクリプト名、もしくは引数のリストが設定されます。 その後は、直前にフォアグラウンドで実行された単純なコマンドがあると、最後の引数が展開されます。また、実行されたコマンドによりエクスポートされ、環境(変数などの空間)に置かれた各コマンドを呼び出すために使用される絶対パス名にも設定されます。 メールのチェック時は、このパラメータはメールのファイル名前を保持します。
自分で訳しておいて何ですが、よく意味がわかりません。英文自体がこねくり回されていて、よく意味がわからんのです。しかし、言わんとしているのは、以下のことだと思います。
- 初期値には実行コマンドを検索するパスの環境変数(つまり文字列
PATH
)が設定されている。 - メール・コマンドを実行すると、最後に開いたメールのファイル名がセットされる。
- 呼び出し直前にコマンドが実行されていた場合、そのコマンドの引数を展開した最後の値がセットされる。
ここで言う「展開」とは、直前のコマンド文をスペース区切りに分け、最後の要素を実行することと思われ、その結果が "_
" という変数にセットされます。使い方もシェルの変数と同じです。変数を使う(展開する)時は $
記号を付け、改行やスペースを扱うときは "$_
" とクォートします。
しかし、その展開方法にも、まだ謎がある(理解できない)部分があります。
例えば @7of9さんの記事で、アスタリスク(*
)を使った場合の面白い挙動がありました。上記説明文にある mail
コマンドの挙動の応用技でしょうか、ls
コマンドで表示された最後のファイル名が取得できます。
$ ls -l
sample1.md
sample2.md
$ echo $_
-l
$ ls *.md
sample1.md
sample2.md
$ echo $_
sample2.md
展開の事例
このように、まだ謎が多く動作もわかりづらいコマンドの1つで、使い始めると妙に便利で、かなりの頻度で利用するようになります。
下記の「展開例」を、シェルの変数の動きを想像しながら見ていくと、うっすらと見える気がします。(← わかってない)
展開例
$ echo hoge
hoge
$ echo $_
hoge
$ echo hoge fuga
hoge fuga
$ echo $_
fuga
$ echo "hoge fuga"
hoge fuga
$ echo $_
hoge fuga
$ echo $_
fuga
echo $ echo "$_"
fuga
$ echo "hoge fuga"
hoge fuga
$ echo "$_"
hoge fuga
$ echo "$_"
hoge fuga
$ ls -la
...(省略)
$ echo $_
-la ← コマンドの実行結果でなくコマンドの引数が展開されて最後がセットされていることに注目
$ # ls の実行結果の最後の行を取得
$ ls *.md | tail -1
sample.md
$ ls *.md && echo $_
sample.md
$ # オプションは効かない(更新日順の -t オプションを使った場合)
$ ls -t *.md | tail -1
old_sample.md
$ ls -t *.md && echo $_
sample.md ← つまり実行結果の最後ではないということ
動作確認済み環境
- macOS Mojave(OSX 10.14.3)
- GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
- macOS HighSierra(OSX 10.13.6)
- GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
- Raspbian GNU/Linux 8(Jessie)
- GNU bash, バージョン 4.3.30(1)-release (arm-unknown-linux-gnueabihf)
あわせて読みたい
- 「mkdirとcdを同時に」@ Qiita
- 「ターミナルの作業が捗るかもな小技5つ」@ Qiita
- 公式 Bash リファレンス@ GNU.org