search
LoginSignup
25
Help us understand the problem. What are the problem?

posted at

updated at

Organization

mkdir と同時に cd でディレクトリ移動させるには(空白入りディレクトリ名の場合)

Bash や Zsh などのシェルで、ディレクトリ作成と同時にディレクトリ移動をしたい場合、一般的に以下のコマンドで可能です。

mkdir my_dir && cd $_

しかし、ディレクトリ名にスペースがあると 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(詳細)
特殊環境変数_(アンダーバー、アンダースコア)の挙動について

bashzsh などのシェルには、"_"(アンダーバー、アンダースコア)という「特殊パラメーター」(特殊環境変数)があります。

この変数には「1つ前のコマンド操作のコマンド内容をスペースに区切り、最後の区切りがパース(処理)された値」が代入されています。

また、現在の設定値を確認するには env コマンドもしくは echo $_ で確認することができます。

これを応用するとディレクトリ作成と同時に移動などに活用できます。

$ mkdir my_very_long_directory_name && cd $_

🐒   Alpine Linux の ash など、特殊パラメーターが使えないシェルもあるので、DockerCI などのスクリプトで使う場合は注意します。

スペース付きディレクトリ名に注意

シェルでスペース付きのパスを扱う場合はクォートするのが常です。この特殊パラメーター $_(アンダーバー)も同様にクォートして使います。

"$_" のようにダブルクォーテーションしないとスペース入りのディレクトリ名で 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 より)

_とは
$_、アンダースコア、アンダーバー)シェルの起動時に、シェルを呼び出すために参照する絶対パス名、または環境設定ファイルで指定され実行されたシェル・スクリプト名、もしくは引数のリストが設定されます。 その後は、直前にフォアグラウンドで実行された単純なコマンドがあると、最後の引数が展開されます。また、実行されたコマンドによりエクスポートされ、環境(変数などの空間)に置かれた各コマンドを呼び出すために使用される絶対パス名にも設定されます。 メールのチェック時は、このパラメータはメールのファイル名前を保持します。

自分で訳しておいて何ですが、よく意味がわかりません。英文自体がこねくり回されていて、よく意味がわからんのです。しかし、言わんとしているのは、以下のことだと思います。

  1. 初期値には実行コマンドを検索するパスの環境変数(つまり文字列 PATH)が設定されている。
  2. メール・コマンドを実行すると、最後に開いたメールのファイル名がセットされる。
  3. 呼び出し直前にコマンドが実行されていた場合、そのコマンドの引数を展開した最後の値がセットされる。

ここで言う**「展開」とは、直前のコマンド文をスペース区切りに分け、最後の要素を実行すること**と思われ、その結果が "_" にセットされます。つまりシェルの変数と同じで、改行やスペースを扱うときは "$_" とクォートします。

しかし、その展開方法にも、まだ謎がある(理解できない)部分があります。

例えば ​@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)

あわせて読みたい

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
25
Help us understand the problem. What are the problem?