在 Pushd-popd 目录列表上迭代时是否需要 if-fi 段?

在 Pushd-popd 目录列表上迭代时是否需要 if-fi 段?

我使用以下代码,它是这个脚本我用它来更新我的 WordPress 网站:

#!/bin/bash
drt="/var/www/html"
for dir in ${drt}/*/; do
    if pushd "$dir"; then
        wp plugin update --all --allow-root
        wp core update --allow-root
        wp language core update --allow-root
        wp theme update --all --allow-root
    popd
    fi
done

当我寻找一种一次性更新所有 WordPress 实例的方法时,我了解了此代码中使用的特定pushd模式。popd

我不清楚为什么这段代码包含一个if-fi段。

我的问题

我可以以某种方式更改语法,以便我拥有基本相同的模式但没有if-fi段吗?

例如,改为:

if pushd "$dir";
popd
    commands
fi

我会有这样的伪代码:

pushd "$dir";
    commands
popd

为什么我问这个

我可以想象我们如何在没有声明if-fi(伪代码)的情况下告诉计算机这样的事情:

for dir in ${drt}/*; do pushd "$drt"; then
    commands
popd

笔记

  1. 您可能希望在答案中包含不同的方法(即根本不包含)pushdpopd

  2. 一个相关问题

答案1

此外@Olorin 写的我认为这里可能存在一些误解。首先,for y in ${x}/*; do pushd "$y"; then结果为

bash:意外标记“then”附近的语法错误


第二,缩进可能会误导您关于实际发生的事情。采用原始代码的正确格式版本:

for y in ${x}/*/
do
    if pushd "$y"
    then
        command1
        command2
        popd
    fi
done

换句话说,所有的 command1command2并且popd仅在初始pushd成功时才运行。如果你改为写

for y in ${x}/*/
do
    pushd "$y"
    command1
    command2
    popd
done

而且没有errexit警卫到位,失败pushdpopd不会影响剧本的其余部分。这可能会导致运行command1command2进入错误的目录,然后可能会弹回另一个目录与此代码无关。这可能会带来灾难性的后果。


最后我想说的是pushd+ 命令 +popd是反模式因为它为语言增加了更多的上下文(因此认知开销和风险),而复杂的上下文已经是一个大问题。解决此问题的最常见方法是将路径(最好是绝对路径)传递给命令,如下所示:

for y in "$x"/*/
do
    command1 "$y"
    command2 "$y"
done

答案2

如果pushd失败会发生什么?如果要在这些目录中运行命令,显然应该pushd在继续执行命令之前检查是否成功。

答案3

使用 if 是防止更改目录失败的合理保护措施。

如果该目录不可执行(无 x 权限),此代码将执行父目录中的命令。

#!/bin/bash
drt="/var/www/html"

for     dir in "${drt}"/*/
do      pushd "$dir"
        pwd
        popd
done

构建几个目录,更改所有者和权限:

$ mkdir -p /var/www/html/{one,two}
$ sudo chown user:user /var/www/html/{one,two}
$ sudo chmod o-x /var/www/html/two
$ ./script
/var/www/html/one ~/temp
/var/www/html/one
~/temp
./script: line 5: pushd: ./var/www/html/two/: Permission denied
~/temp
./script: line 7: popd: directory stack empty

命令 Pushd 发出错误,但命令 pwd 是在 ~/temp 目录中执行的(请注意错误后打印的 ~/temp 的值)。这显然是做错事的风险。与此脚本比较:

#!/bin/bash
    drt="./var/www/html"

    for     dir in "${drt}"/*/
    do      if     pushd "$dir" 2>/dev/null
            then
                   pwd
                   popd
            fi
    done

执行的新脚本:

$ ./script
/var/www/html/one ~/temp
/var/www/html/one
~/temp

或者甚至更好:

#!/bin/bash
drt="./var/www/html"

for     dir in "${drt}"/*/
do      if     pushd "$dir" 2>/dev/null
        then
               pwd
               popd
        else
                echo "Failed to change to dir=$dir" >&2
                exit 7
        fi
done

在执行时,将打印:

$ ./script
/var/www/html/one ~/temp
/var/www/html/one
~/temp
Failed to change to dir=/var/www/html/two/

答案4

使用 gnu-find,您可以:

find ${drt} -maxdepth 1 -type d -execdir command1 ";" -execdir command2 ";"

请注意,并非每个 find 实现都有 -execdir 选项。

相关内容