1. 概要

複数のディレクトリを操作している場合、それらを切り替えることは避けられません。 このチュートリアルでは、ディレクトリの高速かつ効率的なブックマークの手段としてのpushdおよびpopdコマンドについて学習します。

2. pushdpopdが必要なのはなぜですか?

ディレクトリナビゲーションに関しては、 cd コマンドは、ほとんどのユースケースを解決できるデファクトスタンダードです。

ご覧のとおり、次の3種類のディレクトリジャンプを実行できます。

  • 絶対パスを使用して特定のディレクトリに直接ジャンプする CD
  • 親ディレクトリに1レベル上にジャンプします。 CD .. 
  • cd –で最後にアクセスしたディレクトリにジャンプします

ただし、追跡する必要のあるディレクトリの数が増えると、このアプローチはますます非効率的になります。

  • これらのパスのほとんどは、通常、長すぎて入力できません
  • パスオートコンプリート機能を備えたでも、正確なパスプレフィックス記憶することが期待されます。

タイムスタンプt4の後で、パスから1文字を入力せずに、dir1に直接ジャンプできるとしたらどうでしょうか。 pushdコマンドとpopdコマンドの組み合わせにより、このような柔軟性が得られるので、幸運です。

3. シンプルなナビゲーション

私たちの中には、cdコマンドを使用してディレクトリを切り替える習慣を破るのが難しい場合があります。 ですから、これらの新しいツールを探求することを妨げる可能性のある根本的な抵抗を修正しましょう。

3.1. pushdおよびpopd

まず、 cud コマンドと同様に、 pushdコマンドを使用して、絶対パスまたは相対パスを使用して任意のディレクトリにジャンプすることから始めましょう。

$ pushd <directory-path>

また、 pushd コマンドを使用して最後にアクセスしたディレクトリにアクセスする必要がある場合は、引数なしでpopdコマンドを使用できます。

$ popd

この時点で、 cd –コマンドとpopdコマンドが同じ動作を示さないことを明確に理解する必要があります。 前者は最後にアクセスしたディレクトリに移動しますが、後者はpushdコマンドで使用した前のディレクトリに移動します。

3.2. エイリアス

もちろん、エイリアスが私たちの生活を楽にしてくれることは否定できません。 cd コマンドを使用すると、エイリアスを使用してディレクトリ間をすばやくジャンプできます。

$ cd [alias]

ここでは、aliasは任意の有効なディレクトリエイリアスである可能性があります。

  • 。 現在の作業ディレクトリ
  • .. 親ディレクトリを識別するため
  • –最後にアクセスしたディレクトリの場合

内部的には、これらのエイリアスはいくつかの特別なディレクトリ変数を利用します。

  • 現在の作業ディレクトリの$PWD
  • $ {PWD%/ *} は、プレフィックスパラメータ置換を使用して親ディレクトリを識別します
  • 最後にアクセスしたディレクトリの$OLDPWD

そうですね、これらすべてのエイリアスとパス変数はcdコマンドに固有のものではありません。 したがって、pushdコマンドでも使用できます。

$ pushd [alias]

 

さらに、エイリアスまたはディレクトリパスがない場合、pushdはデフォルトのディレクトリを利用します。 そして、それは通常、 $HOME変数によって識別されるユーザーのホームディレクトリです。

これで、 pushd コマンドを使用して、cdコマンドで可能なすべての種類のディレクトリジャンプを実行できるようになりました。 そして、これは、毎回cdコマンドを使用する習慣を断ち切るのに役立ちます。

4. ディレクトリスタックエコシステム

これまで、pushdおよびpopdコマンドの基本的なディレクトリ変更機能を見てきました。 次に、 dirs pushd 、およびpopdコマンドで構成されるディレクトリスタックエコシステム全体の全体的なアプローチを取りましょう。

4.1. 基本

pushdコマンドとpopdコマンドはどちらも、現在の作業ディレクトリを変更するだけではありません。 内部的には、スタックデータ構造を管理して、ディレクトリ間の切り替えを容易にします。 pushdコマンドはスタックの一番上にディレクトリを追加しますが、は、popdコマンドは一番上のからアイテムを削除します。

さて、dirsコマンドを忘れないでください。はディレクトリスタックの内容を文書化する上で重要な役割を果たします。 実際、pushdまたはpopdコマンドを使用するたびに、dirsコマンドのデフォルトの呼び出しも行われます。

$ dirs

次に、これらのコマンドの動作をすべて見て、これらの基本的な概念を要約してみましょう。

管理対象ディレクトリスタックの1つの微妙な特徴に注意する必要があります。 そして、それはスタックの一番上にあるディレクトリに関係します。 dirs コマンドを使用してこのディレクトリスタックの内容を調べると、いつでも現在のディレクトリが常に最上位にあります。

4.2. 列挙

スタックデータ構造の一般的な実装のほとんどは、通常、プッシュまたはポップ機能を介してスタックの最上位要素のみを公開します。 ただし、ディレクトリスタックでは、方向ベースの相対インデックスを介してスタック内のすべてのディレクトリにアクセスすることもできます。

スタックのトップを基準としてカウントしている場合は、正のインデックスが使用されます。 一方、スタックの最下部を参照として使用する場合は、負のインデックスが使用されます

そして、ご覧のとおり、利用可能なインデックス作成には2つのバリエーションがあります。

  • +Nまたは-Nインデックスは、ディレクトリスタックエコシステムの内部にあるコマンド、つまり dirs pushdでのみ使用できます。 ]、および popd
  • 〜+ N または〜-N インデックスは、 cd ls chmodなどの外部コマンドでも使用できます。ディレクトリパスを引数として受け入れる

また、dirsコマンドを-vオプションとともに使用して、インデックスルックアップを実行できます。

$ dirs -v

一方、 +Nまたは-Nインデックスを使用して、ディレクトリコンテンツ内の特定のインデックスでディレクトリの逆引き参照を実行できます。

$ dirs [+N|-N]

4.3. パスバイバリューと 参照渡し

2種類の列挙を調べるもう1つの微妙ですが重要な方法は、ディレクトリ引数が値として渡されているのか、参照として渡されているのかを確認することです。 〜+ N /〜-N列挙は値渡し戦略を使用しますが、 + N/-N列挙は参照渡し戦略を使用します。 。

直感的に言えば、cdlsなどのコマンドは、ディレクトリスタックの内部構造と実装を認識していません。 したがって、すべての外部コマンドは、値渡し戦略によって制限されるため、ディレクトリスタックを変更できないことは理にかなっています。

一方、 dirs pushd popd などのコマンドは、ディレクトリスタックの内部構造をよく認識しています。 その結果、参照渡し戦略を使用してスタックの特定の位置を変更できます。

さらに進んで、スタック内の特定のディレクトリを参照するためにこれらの戦略を利用するいくつかの高度なユースケースを解決します。

5. ディレクトリブックマーク

個人的な仕事と専門的な仕事を切り替えることは、私たちのほとんどにとって非常に一般的です。 このセクションでは、ディレクトリスタックを一連のディレクトリで効率的に初期化する方法を学習します。

5.1. ディレクトリ構造

ホームディレクトリの下に、個人データと仕事関連のデータを保存するための個別のディレクトリがあると想像してみてください。

~
|-- personal
|   |-- family
|   |-- movies
|   `-- pictures
`-- work
    |-- proj1
    |-- proj2
    `-- proj3

このディレクトリ構造を使用して、ディレクトリナビゲーションとディレクトリスタック管理に関連するいくつかの高度なユースケースを解決します。

5.2. -nスイッチ

仕事だけに集中したいとしましょう。 だから、すべての関連する 〜/ work / スタック内のディレクトリは、生産性を向上させる可能性があります。

デフォルトでは、ディレクトリスタックの最上位には、デフォルトのディレクトリ(この場合はホームディレクトリ)が事前に入力されています。

$ dirs -p -v
 0  ~

現在の作業ディレクトリを変更するつもりはないので、これは完璧です。

ここではpushdコマンドを簡単に使用できますが、デフォルトでは、pushdコマンドとpopdコマンドの両方で、現在の作業ディレクトリが変更され、スタックの最上位が変更されます。 そして、それは私たちが現時点で望んでいることではありません。 実際のところ、必要なのは、スタックの残りの部分を変更している間、スタックの最上位をそのままにしておく必要があるということです。

これで、不変条件について、スタックの最上位が常に現在の作業ディレクトリに関連付けられていることがわかりました。 したがって、スタックの最上位の不変性を実現するには、pushdコマンドのディレクトリ変更機能を無効にする方法が必要です。 そして、それが-nスイッチが私たちを助けてくれるところです。

次に、これを使用して、ディレクトリスタックに作業固有のディレクトリを追加します。

$ for dir in $(echo "~/work/proj"{1,2,3}); do
> pushd -n $dir 1>/dev/null
> done

最後に、ディレクトリスタックを調べてみましょう。

$ dirs -p -v
 0  ~
 1  ~/work/proj3
 2  ~/work/proj2
 3  ~/work/proj1

proj1 がすでに完了していることに気付いた以外は、良さそうです。 心配はいりません。リファレンスを使用してポップしてみましょう。

$ popd +3 1>/dev/null
$ dirs -p -v
 0  ~
 1  ~/work/proj3
 2  ~/work/proj2

6. ブックマークのクリーンアップ

時間の経過とともに、現在のフォーカス領域に関連しないいくつかのディレクトリを誤ってスタックにプッシュする可能性があります。 それでは、 Bashスクリプト内でプッシュおよびポップを使用して、スタックからすべてのコンテキスト外のディレクトリブックマークをクリーンアップするプロセスを自動化する方法を学びましょう。

6.1. オートメーション

まず、クリーンアップが必要なディレクトリスタックのサンプルを見てみましょう。

$ dirs -v -p
 0  ~/work/proj1
 1  ~/personal/pictures
 2  ~/work/proj2
 3  ~/personal/family
 4  ~

それでは、クリーンアップ戦略を計画しましょう。

  • に該当しないすべてのディレクトリをポップできます 〜/ ディレクトリ
  • すべてのディレクトリがコンテキスト外の場合は、 〜/ スタックの一番上のディレクトリ

さて、このクリーンアップタスクを自動化することは私たちの側で賢明でしょう。 ただし、そのためには、 dirs、pushd、およびpopdが組み込みのシェル関数であり、シェルの実行中の各インスタンスがディレクトリスタックの独自のコピーを維持することを理解する必要があります。

私たちの目標は現在のインタラクティブなBashセッションのディレクトリスタックをクリーンアップすることなので、コードを〜/.bashrcファイルに含める必要があります。 これは、すべてのインタラクティブなBashセッションがこのスクリプトを呼び出し、すべての関数を現在のbashプロセスにロードするためです。

6.2. スクリプトの実行

最初の引数 $ 1 )を目的のフォーカス領域として扱う clean_dir_bookmarks()というbash関数を作成することから始めましょう。

function clean_dir_bookmarks() {
    focus="$1"
    focus_dir=$HOME/$focus
    typeset -i index=1
}

次に、同じ関数内のディレクトリスタックをループしてみましょう。 また、現在のフォーカス領域に属していないディレクトリが見つかった場合は、現在のディレクトリを変更せずにそのディレクトリをポップします。

while true
do
    stack_index="$(printf "+%d" $index)"
    dir="$(dirs -l $stack_index 2>/dev/null)"
    if [ $? -eq 0 ]
    then
        if [[ ! "$dir" =~ "$focus_dir" ]]
        then
            popd -n $stack_index 1>/dev/null
        fi
    else
        break
    fi
    index=index+1
done

ループにとどまるには、dirsコマンドの正常な実行に依存していることに注意する必要があります。 これが可能なのは、 dirsコマンドを使用して、スタック内に存在しない位置を検査するときに、ゼロ以外の終了コードが保証されるためです。

これまでのところ、スタックの最上位を処理していません。これには、別の処理が必要なためです。 トップディレクトリが現在のフォーカスエリアに属している場合、アクションは不要です。 ただし、コンテキスト外の場合は、次に使用可能なディレクトリに置き換えます。 そして、それが利用できない場合は、単に 〜/ 上部のディレクトリ。

さて、先に進んで、 clean_dir_bookmarks()関数を終了しましょう。

top_dir="$(dirs -l +0 2>/dev/null)"
if [[ ! "$top_dir" =~ "$focus_dir" ]]
then
    dirs +1 1>/dev/null 2>/dev/null
    if [ $? -eq 0 ]
    then
        popd 1>/dev/null
    else
        pushd $focus_dir 1>/dev/null
        popd +1 1>/dev/null
    fi
fi

最後に、関数を使用して必要なクリーンアップを実行しましょう。

$ clean_dir_bookmarks personal
$ dirs -v -p
 0  ~/personal/pictures
 1  ~/personal/family

7. スタックローテーション

スタック内の複数のディレクトリを操作するために、必ずしも明示的なループは必要ありません。 このセクションでは、ディレクトリスタックのローテーションに関連する高度な概念について学習します。

7.1. 不完全な回転サイクル

私たちがいくつかの仕事関連のプロジェクトに取り組んでいたと想像してみてください。それから私たちは少し休憩することにしました。 さらに、休憩中に家族の写真を見始めました。 その結果、作業関連のディレクトリがスタックの一番下にプッシュされます。

$ dirs -v -p
 0  ~/personal/family
 1  ~/personal/pictures
 2  ~/work/proj3
 3  ~/work/proj1

ここで、焦点を仕事に切り替えたいと思いますが、しばらくしてから個人用ディレクトリに再度アクセスしたいと思うかもしれません。 当然、 popd を使用することは、個人ディレクトリが飛び出すような状況では適切なオプションではありません。 ただし、スタックの内容を見ると、作業ディレクトリが[ +2 +3]スタックインデックスの間にあることがわかります。 そして、私たちが本当に必要としているのは、これらすべてのディレクトリを上に移動することです。

これは、ディレクトリを切り替えるための非常に一般的な使用例です。 そして、幸いなことに、 pushd コマンドは、スタックローテーション機能を提供することでこれに対処します。 がpushdでスタックインデックスを使用するときはいつでも、それはインデックスに対して不完全なローテーションサイクルを実行し、スタックのトップはそれによって参照されるディレクトリに変更されますインデックス

そのため、このジョブには正または負のインデックスを使用できます。 それでは、正のインデックスを使用して、ディレクトリスタックをローテーションしてみましょう。

$ pushd +2
$ dirs -v -p
 0  ~/work/proj3
 1  ~/work/proj1
 2  ~/personal/family
 3  ~/personal/pictures

出来上がり! 私たちのディレクトリスタックは、私たちが望んでいたとおりです。

7.2. 完全な回転サイクルを使用した交換

+iの位置に対してpushdを1回呼び出すだけで、Ni時計回りの回転で構成される不完全な回転サイクルを取得できます。 ここで、 pushd を再度呼び出すと、 +(Ni)の位置では、 N-(Ni)=i[を持つ別の不完全なサイクルが発生します。 X141X]時計回りの回転:

$ pushd +i
$ pushd +(N-i)

そして、それらを合計すると、回転の完全なサイクルが1つだけなります。 これは基本的に、スタック内のディレクトリアイテムの位置に正味の変更がないことを意味します。

ここで、すべての作業固有のプロジェクトディレクトリをスタックに保持したいとします。 さらに、ディレクトリスタックを調べると、〜/ work / proj3 ディレクトリを除いて、そのようなすべてのディレクトリがスタックで使用可能であることがわかります。

$ dirs -v -p
 0  ~/work/proj1
 1  ~/work
 2  ~/personal/family
 3  ~/work/proj2

さらに、〜/ personal / family が、proj3ディレクトリを配置する場所を占めていることがわかります。

これで、 popd で位置引数を使用して、スタックから特定のディレクトリを削除できることがわかりました。 しかし、これまでのところ、他のディレクトリを邪魔することなくスタック内の特定の位置を置き換えるのに役立つ高度な置換戦略を学習していません。 それでも、完全なローテーションサイクルについての学習を適用すれば、特定のディレクトリを置き換えることはそれほど難しくありません。

まず、不完全なローテーションサイクルを実行して、〜/ personal /familyディレクトリをスタックの一番上に移動します。

$ pushd +2
$ dirs -v -p
 0  ~/personal/family
 1  ~/work/proj2
 2  ~/work/proj1
 3  ~/work

次に、popdおよびpushdコマンドを順次実行して、スタックの最上位〜/ work /proj3に置き換えましょう。

$ popd
$ pushd ~/work/proj3
$ dirs -v -p
 0  ~/work/proj3
 1  ~/work/proj2
 2  ~/work/proj1
 3  ~/work

最後に、ローテーションサイクルを完了して、〜/ work /proj3が他のディレクトリへのネット変更なしで+2インデックスに移動するようにします。

$ pushd +2
$ dirs -v -p
 0  ~/work/proj1
 1  ~/work
 2  ~/work/proj3
 3  ~/work/proj2

私たちの努力は成功しているようです。

7.3. クローニングと回転を使用した置換

ここで、ディレクトリスタックにすべての作業固有のプロジェクトディレクトリがあるとしましょう。

$ dirs -v -p
 0  ~/work
 1  ~/work/proj1
 2  ~/work/proj3

そして、〜/ work / proj1 ディレクトリを〜/ work / archived / proj1 に移動して、アーカイブすることにしたとします。 残念ながら、このようなシナリオでは、ディレクトリスタック内の既存のディレクトリの名前が変更されません。 自分たちでやらなければならないようです。

さらに、完全なローテーションサイクルを使用する単純な交換戦略ではこれを行うことはできません。 これはです。これは、中間のpopdコマンドが存在しないディレクトリへのディレクトリ変更を試み、失敗して終了するためです。

ここで、クローン作成ベースのローテーションサイクルを活用できます。

$ pushd -n +i

-nスイッチを使用することで、スタックの最上位を保持できるという考え方です。 そして、そのような操作の結果は、単純なローテーションサイクルへの変更であり、ディレクトリのトップが+(Ni)に配置されたディレクトリをそのクローンに置き換えます。

それで、今回は、ターゲットディレクトリをスタックにプッシュすることから始めましょう。

$ pushd ~/work/archived/proj1
$ dirs -v -p
 0  ~/work/archived/proj1
 1  ~/work
 2  ~/work/proj1
 3  ~/work/proj3

次に、クローン作成手法を使用してフルサイクルローテーションを実行しましょう。

$ pushd -n +2
$ pushd -n +2
$ dirs -v -p
 0  ~/work/archived/proj1
 1  ~/work
 2  ~/work/archived/proj1
 3  ~/work/proj3

最後に、スタックの一番上から重複アイテムをポップオフしましょう。

$ popd
$ dirs -v -p
 0  ~/work
 1  ~/work/archived/proj1
 2  ~/work/proj3

わーい! やりました。

8. 結論

このチュートリアルでは、pushdおよびpopdコマンドの必要性を調査することから始めました。 そこから、すべてのディレクトリナビゲーションのユースケースでcdコマンドに依存する習慣を断ち切ることができました。

その後、ディレクトリスタックに関連する概念の詳細な調査に専念しただけでなく、それらを適用して、いくつかの一般的で高度なユースケースを解決しました。