有没有更好的方法来展开深度推送堆栈?

有没有更好的方法来展开深度推送堆栈?

我有时会遇到 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

重要的位是两个pushds,以popd连续的两个匹配的 s 结尾。最后一句话总是让我困扰;似乎应该有一种更好的方法来表达从堆栈中弹出任意数量的项目并切换回原始目录的意图。

我搜索了 Bash 手册页并尝试了popd +n-n参数的变体,但它们似乎所做的就是删除单身的每次都从堆栈中取出项目。这不是我想要做的;我正在尝试将目录堆栈展开,无论其深度有多少条目,回到之前的状态。

OLDPWD=$(pwd)如果没有切换到我在某个地方保存的范例cd $OLDPWD,最后是否有更好的方法来做到这一点?

答案1

在脚本中使用该目录是有问题的,因为它存在风险。popd不返回原目录,而是返回同名目录。如果原始目录已移动,您的脚本将中途切换位置。这似乎是一个遥远的风险,但迟早会发生这种情况将要发生这种情况,脚本的用户就会诅咒你。

如果您想暂时在不同的目录中运行,最好的方法是在子 shell 中运行该部分脚本。

( 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
)

仅出于示例目的,我将展示替代方法,这些方法在这里不是必需的,但在子 shell 不适合的类似情况下可能很有用,例如因为您想要设置一些变量并在返回到原始版本后使它们可用目录。

您实际上可能不需要切换到不同的目录,或者只需要在单个命令的持续时间内切换到不同的目录。如果您使用的是 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

对于没有切换到不同目录选项的命令,您可以使用范围较窄的子 shell。

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调用,而不是调用您不想返回的目录。cdcdpushd

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

但我真的建议根本不要使用pushdand 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"

答案2

如果您对要弹出的目录堆栈中的级别有一个脑海中的印象,您可以使用一个函数:

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

答案3

您可以简单地在开始工作之前启动一个新的子 shell,然后在结束时退出它,返回到原始 shell 和目录。

但最好保留您的目录历史记录,因为pushd稍后您经常需要重复相同的操作。我实际上是cd等效的别名pushd,然后使用其他函数轻松导航目录堆栈而不会丢失任何内容。相反,您可以做一些简单的事情,例如列出您的目录堆栈并询问要转到哪个函数:

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

相关内容