从来没想过这种事会发生在我身上,但就是这样。¯\_(ツ)_/¯
我从错误目录内的存储库运行了构建脚本,而没有先查看源代码。这是脚本Scripts/BuildLocalWheelLinux.sh
:
cd ../Dependencies/cpython
mkdir debug
cd debug
../configure --with-pydebug --enable-shared
make
cd ../../..
cd ..
mkdir -p cmake-build-local
cd cmake-build-local
rm -rf *
cmake .. -DMVDIST_ONLY=True -DMVPY_VERSION=0 -DMVDPG_VERSION=local_build
make -j
cd ..
cd Distribution
python3 BuildPythonWheel.py ../cmake-build-local/[redacted]/core.so 0
python3 -m ensurepip
python3 -m pip install --upgrade pip
[more pip install stuff]
python3 -m setup bdist_wheel --plat-name manylinux1_x86_64 --dist-dir ../dist
cd ..
cd Scripts
危险的部分似乎是
mkdir -p cmake-build-local
cd cmake-build-local
rm -rf *
但转念一想,似乎也确实不可能出错。
您应该运行此脚本的方式是cd Scripts; ./BuildLocalWheelLinux.sh
。当我第一次运行它时,它在最后一行显示了一个错误(正如我后来了解到的那样)。我很着急,所以我想“也许文档已经过时了,我会尝试从项目根目录运行。所以我跑了./Scripts/BuildLocalWheelLinux.sh
。突然,vscodes 主题和缩放级别发生了变化,我的 zsh 终端配置被重置,终端字体被更改设置为默认值,一旦我意识到发生了什么,我就按 Ctrl+C。
还剩下一些文件,但它们没有明显的模式:
$ ls -la
total 216
drwx------ 27 felix felix 4096 May 12 18:08 .
drwxr-xr-x 3 root root 4096 Apr 15 16:39 ..
-rw------- 1 felix felix 12752 Apr 19 11:07 .bash_history
-rw-r--r-- 1 felix felix 3980 Apr 15 13:40 .bashrc
drwxrwxrwx 7 felix felix 4096 May 12 18:25 .cache
drwx------ 8 felix felix 4096 May 12 18:26 .config
drwx------ 3 root root 4096 Apr 13 21:40 .dbus
drwx------ 2 felix felix 4096 Apr 30 12:18 .docker
drwxr-xr-x 8 felix felix 4096 Apr 15 13:40 .dotfiles
-rw------- 1 felix felix 8980 Apr 13 18:10 examples.desktop
-rw-r--r-- 1 felix felix 196 Apr 19 15:19 .gitconfig
-rw-r--r-- 1 felix felix 55 Apr 16 13:56 .gitconfig.old
-rw-r--r-- 1 felix felix 1040 Apr 15 13:40 .gitmodules
drwx------ 3 felix felix 4096 May 6 10:10 .gnupg
-rw-r--r-- 1 felix felix 1848 May 5 14:24 heartbeat.tcl
-rw------- 1 felix felix 1610 Apr 13 20:36 .ICEauthority
drwxr-xr-x 5 felix felix 4096 Apr 21 16:39 .ipython
drwxr-xr-x 2 felix felix 4096 May 4 09:35 .jupyter
-rw------- 1 felix felix 161 Apr 27 14:23 .lesshst
drwx------ 3 felix felix 4096 May 12 18:08 .local
-rw-r--r-- 1 felix felix 140 Apr 29 17:54 minicom.log
drwx------ 5 felix felix 4096 Apr 13 18:25 .mozilla
drwxr-xr-x 2 felix felix 4096 Apr 13 18:10 Music
drwxr-xr-x 6 felix felix 4096 May 12 17:16 Nextcloud
-rw-r--r-- 1 felix felix 52 Apr 16 11:43 .nix-channels
-rw------- 1 felix felix 1681 Apr 20 10:33 nohup.out
drwx------ 3 felix felix 4096 Apr 15 11:16 .pki
-rw------- 1 felix felix 946 Apr 16 11:43 .profile
drwxr-xr-x 2 felix felix 4096 Apr 13 18:10 Public
drwxr-xr-x 2 felix felix 4096 May 12 18:08 .pylint.d
-rw------- 1 felix felix 1984 May 12 18:06 .pythonhist
-rw-r--r-- 1 felix felix 2443 Apr 19 13:40 README.md
drwxr-xr-x 13 felix felix 4096 May 12 18:08 repos
drwxr-xr-x 6 felix felix 4096 Apr 19 11:08 snap
drwx------ 3 felix felix 4096 May 5 15:33 .ssh
drwxr-xr-x 5 felix felix 4096 Apr 26 17:39 .stm32cubeide
drwxr-xr-x 5 felix felix 4096 May 5 15:52 .stm32cubemx
drwxr-xr-x 2 felix felix 4096 Apr 23 11:44 .stmcube
drwxr-xr-x 2 felix felix 4096 Apr 13 18:10 Templates
drwxr-xr-x 3 felix felix 4096 Apr 19 11:57 test
drwxr-xr-x 2 felix felix 4096 Apr 13 18:10 Videos
-rw------- 1 felix felix 14313 May 12 10:45 .viminfo
-rw-r--r-- 1 felix felix 816 Apr 15 13:40 .vimrc
drwxr-xr-x 3 felix felix 4096 Apr 16 12:08 .vscode
-rw-r--r-- 1 felix felix 2321 Apr 19 18:47 weird_bug.txt
-rw-r--r-- 1 felix felix 162 Apr 15 13:40 .xprofile
.config
以及一些标准 XDG 目录(例如图片和桌面)都消失了,但 .bashrc 仍然存在。.nix-channels
仍然在那里,但.nix-defexpr
被核武器摧毁了。
所以,这让我想到两个问题:
- 什么地方出了错?我想修复此构建脚本并制作 PR 以防止将来发生这种情况。
- 文件被删除的顺序是什么?显然不是按字母顺序排列,而是
*
按字母顺序展开,所以看起来这里发生了其他事情。
答案1
哎哟。你不是第一个受害者。
什么地方出了错?
从您的主目录开始,例如/home/felix
,甚至从/home/felix/src
或开始/home/felix/Downloads/src
。
cd ../Dependencies/cpython
失败,因为没有../Dependencies
。
mkdir debug cd debug
您现在位于debug
起始目录的子目录中。
../configure --with-pydebug --enable-shared make
不执行任何操作,因为没有../configure
或make
。
cd ../../.. cd ..
如果开始时目录深度不超过三层,达到cd debug
第四层时,当前目录现在是根目录。如果您开始时有四个目录级别,则当前目录是 now /home
。
mkdir -p cmake-build-local
这会失败,因为您没有写入/
或 的权限/home
。
cd cmake-build-local
由于没有目录,因此失败cmake-build-local
。
我们现在要……
文件被删除的顺序是什么?
rm -rf *
这会尝试递归删除当前目录中的每个文件,即/
或/home
。主目录按字母顺序枚举,但下面的文件按目录遍历的任意顺序枚举。它的顺序相同ls --sort=none
(除非rm
由于某种原因决定使用不同的顺序)。请注意,此顺序通常不会保留在备份中,并且在目录中创建或删除文件时可能会更改。
如何修复脚本
首先,几乎所有 shell 脚本都应该位于set -e
顶部附近。set -e
如果命令失败,则会导致脚本中止。 (如果退出状态非零,命令就会失败。)set -e
并不是万能药,因为在某些情况下它不会生效。但这是您可以期望的最低限度,而且它在这里会做正确的事情。
(脚本也应该以舍邦行来指示要使用哪个 shell,例如#!/bin/sh
或#!/bin/bash
。但这对解决这个问题没有帮助。)
rm -rf *
,或类似的变体rm -rf $foo.*
(如果$foo
结果是空的怎么办?),是脆弱的。在这里,而不是
mkdir -p cmake-build-local
cd cmake-build-local
rm -rf *
删除并重新创建目录会更可靠。 (这不会保留目录的权限,但在这里这不是问题。)
rm -rf cmake-build-local
mkdir cmake-build-local
cd cmake-build-local
make clean
另一种方法对于删除错误的文件更强大,但对于删除丢失的文件更脆弱:通过运行具有rm
已知构建目标和已知扩展名的命令(例如rm *.o
可以),仅删除已知已构建的文件。
答案2
跟踪您的cd
调用,假设我们正在运行脚本~/Distribution/Scripts
并假设每个调用都cd
成功:
cd ../Dependencies/cpython
我们现在在~/Distribution/Dependencies/cpython
.
mkdir debug
cd debug
我们现在在~/Distribution/Dependencies/cpython/debug
.
cd ../../..
我们现在在~/Distribution
.
cd ..
我们现在位于您的主目录中。
mkdir -p cmake-build-local
cd cmake-build-local
我们现在在~/cmake-build-local
.这就是你奔跑的地方rm -rf *
。
cd ..
我们现在回到您的主目录
cd Distribution
我们现在在~/Distribution
.
cd ..
cd Scripts
我们现在处于~/Scripts
(您会因此得到一个错误,因为您比您预期的级别高一级)。
接着。您尝试运行相同的脚本,但来自~/Distribution
.
cd ../Dependencies/cpython
这失败了。这让你仍然处于~/Distribution
.
mkdir debug
cd debug
你现在在~/Distribution/debug
.
cd ../../..
您现在位于~/..
(可能位于/home
)。
cd ..
你很可能/
现在就在。
cd Distribution
mkdir -p cmake-build-local
cd cmake-build-local
这些可能会由于“没有这样的文件或目录”和“权限被拒绝”而失败。
rm -rf *
您仍在/
目录中,您的rm
命令将尝试删除整个文件系统中的每个文件。权限仅允许删除位于您有写入权限的目录中的文件,因此您可能只会丢失/tmp
主目录中的文件。
命令行上列出的参数rm
将按照扩展它们的顺序进行处理*
(字典顺序,即,bin
,boot
,cdrom
,dev
,etc
等)。然后列出的每个目录将按照“目录顺序”(未排序)进行递归处理。
你应该做什么:
#!/bin/sh
topdir=$HOME/Distribution
mkdir -p "$topdir/Dependencies/cpython/debug"
(
cd "$topdir/Dependencies/cpython/debug" || exit 1
../configure --with-pydebug --enable-shared
make
)
rm -rf "$topdir/cmake-build-local"
mkdir -p "$topdir/cmake-build-local"
(
cd "$topdir/cmake-build-local" || exit 1
cmake .. -DMVDIST_ONLY=True -DMVPY_VERSION=0 -DMVDPG_VERSION=local_build
make -j
)
(
cd "$topdir" || exit 1
python3 BuildPythonWheel.py ../cmake-build-local/[redacted]/core.so 0
python3 -m ensurepip
python3 -m pip install --upgrade pip
python3 -m setup bdist_wheel --plat-name manylinux1_x86_64 --dist-dir ../dist
)
每个单独的子 shell 中的工作目录( ... )
都是该子 shell 的本地目录。子 shell 中的初始值cd
不会影响“外部”环境的工作目录。代码的其余部分使用不依赖于用户 shell 会话的初始工作目录的绝对路径名。特别要注意的是,该rm
命令不会盲目扩展*
,而是删除通过绝对路径指定的特定目录(如果该目录不存在,这不会出现严重错误)。
答案3
如果脚本假设它是从 inside 运行的Scripts
,那么这些都不会执行脚本作者的预期:
cd ../Dependencies/cpython
mkdir debug
cd debug
cd ../../..
cd ..
mkdir -p cmake-build-local
cd cmake-build-local
第一个cd
会失败,第二个cd
会移动到一个文件夹中,然后可能会将 cd ../../..
您cd ..
放在主目录上方的某个位置(可能在该/home
目录中,您通常无权在其中创建任何内容,因此mkdir
后续cd
会失败)。然后*
可能扩展到您的主目录,因此rm -rf
对其进行操作,递归到内容,这将解释删除的随机顺序(目录条目不按任何特定顺序排序)。