Bash 没有子 shell 的 "exit" 脚本不退出(我认为)

Bash script not exiting with "exit" without subshells (I think)

我正在使用 stow 来管理我的点文件,我正在编写一个可以自动设置我的电脑和程序包的脚本。我的所有配置文件都在 config 文件夹中,这就是 stow 文件夹。我基本上做的是获取该文件夹中所有文件夹(包)的名称并将它们存放到我的主目录中。我还有一个函数,可以简单地告诉我是否有错误并退出脚本。

我正在 运行在 Arch Linux 上安装这个脚本,最近在 tty 上安装(我还没有安装 window 管理器)。当它去存放 bash 时,它失败了,因为 .bashrc 已经存在于 home 中。它给了我错误信息,但它没有退出,我也找不到原因。我不认为我 运行 在子 shell 上使用 error 函数,就像我从其他有这个问题的人那里看到的那样,除非我在这里遗漏了什么...

函数如下:

error () {
    echo "!!  !!"
    exit 1
}

后来我有这样的事情:

ls -1 config | while read line; do
    stow -d config -t ~ -R "${line}" || error "Failed to stow ${line}."
done

下面是从创建函数到加载的全部代码:

step () {
    echo "> "
}

substep () {
    echo "--> "
}

error () {
    echo "!!  !!"
    exit 1
}

success () {
    echo "**  **"
}

commandexists () {
    # Check if a command exists
    command -v  &> /dev/null
    if [[ $? -eq 0 ]]; then
        return 0
    else
        return 1
    fi
}

pkg-install () {
    pkg="${1:?"Error: pkg parameter unset."}"

    step "${msg_install}: ${pkg}"

    substep "Installing ${pkg} from the Arch Linux Repositories."
    sudo pacman -Sy --noconfirm --needed "$pkg"

    if [[ $? -ne 0 ]]; then
        substep "Installing ${pkg} from the Arch User Repositories."
        yay -Sy --noconfirm --needed "$pkg" || error "${msg_fail_install}: ${pkg}"
    fi

    success "${msg_success_install}: ${pkg}"
}

# Stop installation if yay is not installed
commandexists yay || error "Yay is not installed! Please install yay and try again."

# stow
pkg-install stow

step "Stowing config files"
ls -1 config | while read line; do
    substep "Stowing ${line}"
    stow -d config -t ~ -R "${line}" || error "Failed to stow ${line}."
done
success "Successfully stowed all config files"

如您所见,在存放之前,它会通过检查命令是否存在来检查 yay 是否已安装。如果没有,它会给我一个错误并退出。当我在另一台未安装 yay 的电脑上 运行 时,它按预期工作。它告诉我 yay 没有安装并停在那里。但是为什么当我在安装了 yay 的电脑上 运行 它会忽略 stow 部分的 exit 命令?

在这种特殊情况下,您可以将 while 循环保留在父 shell 中,同时将 ls 移动到子 shell 中:

while IFS= read -r line; do
    stow -d config -t ~ -R "${line}" || error "Failed to stow ${line}."
done < <(ls -1 config)

(使用 IFS= 可让您的代码正确处理以空格开头或结尾的名称;使用 -r 可使其正确处理包含文字反斜杠的名称。


...但实际上不要这样做; ls output is unsuited to programmatic use.

改为使用 glob:

for path in config/*; do
    [[ -e $path || -L $path ]] || continue # detect case where config/ was empty
    file=${path%config/}                   # strip the config/ off the name
    stow -d config -t ~ -R "$file" || error "Failed to stow $file"
done