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つのpushd
sであり、2つの一致するpopd
sが連続して終了します。その最後の行はいつも私を悩ませました。任意の数のアイテムをスタックからポップして元のディレクトリに戻す意図を表現するためのより良い方法があるはずです。
Bashのmanページを検索し、_popd +n
_および_-n
_引数のバリアントを試しましたが、毎回スタックからsingleアイテムを削除するだけのようです。それは私がやろうとしていることではありません。ディレクトリスタックを巻き戻そうとしていますが、多くのエントリが深くても、以前の状態に戻ります。
OLDPWD=$(pwd)
をどこかに保存し、最後に_cd $OLDPWD
_を保存するパラダイムに切り替える以外に、これを行うためのより良い方法はありますか?
スクリプトでのディレクトリの使用は、リスクが発生しやすいため、疑わしいものです。 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ツールを使用している場合、tar
とmake
の両方が-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
しかし、pushd
とpopd
をまったく使用しないことを強くお勧めします。これらはインタラクティブな使用を目的としており、スクリプトでは何よりも混乱する傾向があります。現在のディレクトリの場所を保存する場合は、変数に保存します。これにより、スクリプトが読みやすくなります。これにより、読者は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
作業を開始する前に新しい子シェルを開始し、最後に終了して、元のシェルとディレクトリに戻ることができます。
ただし、後で同じpushd
を繰り返す必要があることが多いため、ディレクトリ履歴を保持するのがおそらく最善です。私は実際にcd
をpushd
に相当するものにエイリアスし、他の関数を使用してdirstackを失うことなく簡単にナビゲートします。それよりも、dirstackを一覧表示して、どちらに移動するかを尋ねる関数など、簡単なことを行うことができます。
cdp(){
select v in $(dirs -l -p)
do pushd "$v"
break
done
}