AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 454694
Accepted
Raphael
Raphael
Asked: 2018-07-12 04:57:51 +0800 CST2018-07-12 04:57:51 +0800 CST 2018-07-12 04:57:51 +0800 CST

我如何加强 bash 脚本在将来更改时不会造成伤害?

  • 772

所以,我删除了我的主文件夹(或者更准确地说,我有写权限的所有文件)。发生的事情是我有

build="build"
...
rm -rf "${build}/"*
...
<do other things with $build>

在 bash 脚本中,并且在不再需要之后$build,删除声明及其所有用法——但是rm. Bash 愉快地扩展为rm -rf /*. 是的。

我觉得自己很愚蠢,安装了备份,重新完成了我丢失的工作。试图摆脱耻辱。

现在,我想知道:编写 bash 脚本的技术是什么,这样就不会发生这种错误,或者至少不太可能发生这种错误?例如,我是否写过

FileUtils.rm_rf("#{build}/*")

在 Ruby 脚本中,解释器会抱怨build没有被声明,所以语言保护了我。

我在 bash 中考虑的内容,除了 corraling rm(正如相关问题中的许多答案所提到的,这并非没有问题):

  1. rm -rf "./${build}/"*
    那会杀死我当前的工作(一个 Git 存储库),但没有别的。
  2. rm在当前目录之外操作时,它的变体/参数化需要交互。(找不到任何东西。)类似的效果。

是这样,还是有其他方法可以编写在这个意义上“健壮”的 bash 脚本?

bash shell-script
  • 7 7 个回答
  • 4613 Views

7 个回答

  • Voted
  1. Best Answer
    Kusalananda
    2018-07-12T05:00:22+08:002018-07-12T05:00:22+08:00
    set -u
    

    或者

    set -o nounset
    

    这将使当前的 shell 将未设置变量的扩展视为错误:

    $ unset build
    $ set -u
    $ rm -rf "$build"/*
    bash: build: unbound variable
    

    set -u并且set -o nounset是POSIX shell 选项。

    但是,空值不会触发错误。

    为此,使用

    $ rm -rf "${build:?Error, variable is empty or unset}"/*
    bash: build: Error, variable is empty or unset
    

    的扩展${variable:?word}将扩展为的值,variable除非它为空或未设置。如果它为空或未设置,word则会在标准错误中显示,并且 shell 会将扩展视为错误(不会执行命令,如果在非交互式 shell 中运行,则会终止)。离开:会仅针对未设置的值触发错误,就像 under 一样set -u。

    ${variable:?word}是一个POSIX 参数扩展。

    set -e除非(或set -o errexit)也生效,否则这些都不会导致交互式 shell 终止。${variable:?word}如果变量为空或未设置,则导致脚本退出。set -u如果与 . 一起使用会导致脚本退出set -e。


    至于你的第二个问题。没有办法限制rm不能在当前目录之外工作。

    的 GNU 实现rm有一个--one-file-system选项可以阻止它递归地删除已挂载的文件系统,但是我相信我们可以在不将rm调用包装在一个实际检查参数的函数中的情况下得到尽可能接近的结果。


    作为旁注: ${build}完全等同于$build除非扩展作为字符串的一部分出现,其中紧随其后的字符是变量名中的有效字符,例如 in "${build}x"。

    • 78
  2. Centimane
    2018-07-12T10:36:04+08:002018-07-12T10:36:04+08:00

    我将建议使用test/[ ]

    如果您这样编写脚本,您将是安全的:

    build="build"
    ...
    [ -n "${build}" ] || exit 1
    rm -rf "${build}/"*
    ...
    

    [ -n "${build}" ]检查是否"${build}"为非零长度字符串。

    ||是 bash 中的逻辑 OR 运算符。如果第一个命令失败,它将导致另一个命令运行。

    这样,就${build}一直是空的/未定义的/等。该脚本将退出(返回代码为 1,这是一个一般错误)。

    如果您删除所有用途,这也可以保护您,${build}因为这[ -n "" ]将永远是错误的。


    使用test/的优点[ ]是它还可以使用许多其他更有意义的检查。

    例如:

    [ -f FILE ] True if FILE exists and is a regular file.
    [ -d FILE ] True if FILE exists and is a directory.
    [ -O FILE ] True if FILE exists and is owned by the effective user ID.
    
    • 12
  3. user117529
    2018-07-13T00:07:01+08:002018-07-13T00:07:01+08:00

    在您的特定情况下,我过去重新设计了“删除”以移动文件/目录(假设 /tmp 与您的目录位于同一分区):

    # mktemp -d is also a good, reliable choice
    trashdir=/tmp/.trash-$USER/trash-`date`
    mkdir -p "$trashdir"
    ...
    mv "${build}"/* "$trashdir"
    ...
    

    在幕后,这会将顶级文件/目录引用从源目录结构移动$trashdir到同一分区上的目标目录结构,并且不会花时间遍历目录结构并立即释放每个文件的磁盘块。这会在系统处于活动使用状态时产生更快的清理速度,以换取稍慢的重新启动(/tmp 在重新启动时被清理)。

    或者,定期清理 /tmp/.trash-$USER 的 cron 条目将防止 /tmp 填满,用于消耗大量磁盘空间的进程(例如,构建)。如果您的目录与 /tmp 位于不同的分区上,您可以在您的分区上创建一个类似 /tmp 的目录,然后让 cron 清理它。

    但是,最重要的是,如果您以任何方式搞砸了变量,您可以在清理发生之前恢复内容。

    • 4
  4. rackandboneman
    2018-07-12T21:56:27+08:002018-07-12T21:56:27+08:00

    当变量未初始化时,使用 bash 参数替换来分配默认值,例如:

    rm -rf ${变量:-“/不存在”}

    • 2
  5. niya3
    2018-07-25T10:25:37+08:002018-07-25T10:25:37+08:00

    我总是尝试用一行开始我的 Bash 脚本#!/bin/bash -ue。

    -e意思是“在第一个未解决的错误上失败”;

    -u意思是“第一次使用未声明的变量时失败”。

    在伟大的文章Use the Unofficial Bash Strict Mode (Unless You Looove Debugging)中找到更多详细信息。作者还建议使用set -o pipefail; IFS=$'\n\t',但出于我的目的,这是矫枉过正的。

    • 1
  6. eschwartz
    2018-07-25T11:57:55+08:002018-07-25T11:57:55+08:00

    检查您的变量是否已设置的一般建议是防止此类问题的有用工具。但在这种情况下,有一个更简单的解决方案。

    很可能不需要 glob$build目录的内容来删除它们,而不是实际的$build目录本身。因此,如果您跳过了无关项*,那么未设置的值将变成rm -rf /默认情况下过去十年中大多数 rm 实现将拒绝执行的值(除非您使用 GNU rm 的--no-preserve-root选项禁用此保护)。

    跳过尾随/也会rm ''导致错误消息:

    rm: can't remove '': No such file or directory
    

    即使您的 rm 命令没有对/.

    • 1
  7. Rich
    2018-07-12T14:03:40+08:002018-07-12T14:03:40+08:00

    您正在考虑编程语言术语,但bash是一种脚本语言 :-) 因此,对于完全不同的语言范例,请使用完全不同的指令范例。

    在这种情况下:

    rmdir ${build}
    

    由于rmdir将拒绝删除非空目录,因此您需要先删除成员。你知道那些成员是什么,对吧?它们可能是脚本的参数,或者是从参数派生的,所以:

    rm -rf ${build}/${parameter}
    rmdir ${build}
    

    现在,如果您将一些其他文件或目录放入其中,例如临时文件或您不应该放入的其他文件,rmdir则会引发错误。妥善处理,然后:

    rmdir ${build} || build_dir_not_empty "${build}"
    

    这种思维方式对我很有帮助,因为……是的,去过那里,做到了。

    • -3

相关问题

  • 从文本文件传递变量的奇怪问题

  • 虽然行读取保持转义空间?

  • MySQL Select with function IN () with bash array

  • `tee` 和 `bash` 进程替换顺序

  • 运行一个非常慢的脚本直到它成功

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    ssh 无法协商:“找不到匹配的密码”,正在拒绝 cbc

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    如何卸载内核模块“nvidia-drm”?

    • 13 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add 返回:“连接代理时出错:没有这样的文件或目录” 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya 为什么 Linux 使用 LF 作为换行符? 2017-12-20 05:48:21 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve