web-dev-qa-db-ja.com

深くプッシュされたスタックをほどくためのより良い方法はありますか?

Bashスクリプトで次のようなことが起こる状況に遭遇することがあります。

_pushd /tmp
wget -q http://download.redis.io/redis-stable.tar.gz
tar xzf redis-stable.tar.gz
pushd redis-stable
make
Sudo make install
popd; popd
_

重要なビットは2つのpushdsであり、2つの一致するpopdsが連続して終了します。その最後の行はいつも私を悩ませました。任意の数のアイテムをスタックからポップして元のディレクトリに戻す意図を表現するためのより良い方法があるはずです。

Bashのmanページを検索し、_popd +n_および_-n_引数のバリアントを試しましたが、毎回スタックからsingleアイテムを削除するだけのようです。それは私がやろうとしていることではありません。ディレクトリスタックを巻き戻そうとしていますが、多くのエントリが深くても、以前の状態に戻ります。

OLDPWD=$(pwd)をどこかに保存し、最後に_cd $OLDPWD_を保存するパラダイムに切り替える以外に、これを行うためのより良い方法はありますか?

1
smitelli

スクリプトでのディレクトリの使用は、リスクが発生しやすいため、疑わしいものです。 popdは元のディレクトリに戻るのではなく、同じ名前のディレクトリに戻ります。元のディレクトリが移動された場合、スクリプトは途中で場所を切り替えます。これはリモートリスクのように見えるかもしれませんが、遅かれ早かれこれが発生し、スクリプトのユーザーがあなたを呪うでしょう。

別のディレクトリで一時的に操作する場合は、スクリプトのその部分をサブシェルで実行するのが最善の方法です。

( set -e
  cd "${TMPDIR:-/tmp}"
  wget -q http://download.redis.io/redis-stable.tar.gz
  tar xzf redis-stable.tar.gz
  cd redis-stable
  make
  Sudo make install
)

例のために、ここでは必要ありませんが、サブシェルが適していない同様の状況で役立つ可能性のある代替アプローチを示します。いくつかの変数を設定し、元のディレクトリに戻った後にそれらを使用できるようにするためです。

実際には別のディレクトリに切り替える必要はないかもしれませんし、個々のコマンドの実行中だけであるかもしれません。 GNUツールを使用している場合、tarmakeの両方が-C引数をサポートしてディレクトリに切り替えます。元のディレクトリとは異なり、スクリプトでそのディレクトリを作成している場合ディレクトリ、それはその場所が移動しないという合理的な仮定です。

set -e
: "${TMPDIR:=/tmp}"
wget -q -O "$TMPDIR/redis-stable.tar.gz" http://download.redis.io/redis-stable.tar.gz
tar xzf redis-stable.tar.gz -C "$TMPDIR"
make -C "$TMPDIR/redis-stable"
Sudo make -C "$TMPDIR/redis-stable" install

別のディレクトリに切り替えるオプションがないコマンドでは、スコープが狭いサブシェルを使用できます。

set -e
: "${TMPDIR:=/tmp}"
( cd "$TMPDIR"
  wget -q http://download.redis.io/redis-stable.tar.gz
  tar xzf redis-stable.tar.gz -C "$TMPDIR"
)
( cd "$TMPDIR/redis-stable"
  make
  Sudo make install
)

本当に前後に切り替える必要がある場合、popdへの複数の呼び出しに対する明らかな改善は、戻りたくないディレクトリに対してcdを呼び出す代わりに、cdを意味するときにpushdを呼び出すことです。

set -e
pushd "${TMPDIR:-/tmp}"
wget -q http://download.redis.io/redis-stable.tar.gz
tar xzf redis-stable.tar.gz
cd redis-stable
make
Sudo make install
popd

しかし、pushdpopdをまったく使用しないことを強くお勧めします。これらはインタラクティブな使用を目的としており、スクリプトでは何よりも混乱する傾向があります。現在のディレクトリの場所を保存する場合は、変数に保存します。これにより、スクリプトが読みやすくなります。これにより、読者はpopd呼び出しが返される場所を把握する必要がなくなります。

set -e
original_directory="$PWD"
cd "${TMPDIR:-/tmp}"
wget -q http://download.redis.io/redis-stable.tar.gz
tar xzf redis-stable.tar.gz
cd redis-stable
make
Sudo make install
cd "$original_directory"

ポップアップしたいdirsスタックのレベルのイメージがある場合は、次の関数を使用できます。

function mpopd {
  n=$1
  while [[ $n > 0 ]]
  do
    popd
    n=$((n-1))
  done
}

サンプル実行:

[schaller@Host smitelli] dirs
~/tmp/smitelli
[schaller@Host smitelli] pushd tmp/
~/tmp/smitelli/tmp ~/tmp/smitelli
[schaller@Host tmp] pushd redis-stable/
~/tmp/smitelli/tmp/redis-stable ~/tmp/smitelli/tmp ~/tmp/smitelli
[schaller@Host redis-stable] mpopd 2
~/tmp/smitelli/tmp ~/tmp/smitelli
~/tmp/smitelli
1
Jeff Schaller

作業を開始する前に新しい子シェルを開始し、最後に終了して、元のシェルとディレクトリに戻ることができます。

ただし、後で同じpushdを繰り返す必要があることが多いため、ディレクトリ履歴を保持するのがおそらく最善です。私は実際にcdpushdに相当するものにエイリアスし、他の関数を使用してdirstackを失うことなく簡単にナビゲートします。それよりも、dirstackを一覧表示して、どちらに移動するかを尋ねる関数など、簡単なことを行うことができます。

cdp(){
  select v in $(dirs -l -p)
  do pushd "$v"
     break
  done
}
0
meuh