在 Bash 脚本中排除 zip 中的目录和文件

在 Bash 脚本中排除 zip 中的目录和文件

我正在编写 bash 脚本,该脚本应该存档一些包含 python 代码的目录。我想避免存档 .pyc 文件、__pycache__目录、IPython 笔记本及其检查点。但是,__pycache__子目录无论如何都会被存档。

剧本

#!/bin/bash

py_filelist='__main__.py pkg'
py_exclude='*.pyc *.ipynb \*__pycache__\* *ipynb_checkpoints*'  # <<-- Have tried many variants, nothing helps
archive_name=archive.zip

zip -rp $archive_name $py_filelist -x $py_exclude

运行时,我得到以下输出

$ bash -x script.sh

+ zip -rp archive.zip __main__.py pkg -x '*.pyc' '*.ipynb' '\*__pycache__\*' '*ipynb-checkpoints*'
adding: __main__.py (deflated 61%)
adding: pkg/ (stored 0%)
adding: pkg/file.py (deflated 62%)
adding: pkg/engine/ (stored 0%)
adding: pkg/engine/tools.py (deflated 73%)
...
adding: pkg/engine/templates/__pycache__/ (stored 0%)
...
adding: pkg/__pycache__/ (stored 0%)

因此,只有 .pyc 和 .ipynb 文件被排除。

__pycache__在脚本中指定排除模式的正确方法是什么?

答案1

使用 shell 变量的方式很脆弱、容易出错并且是一件坏事,尤其是在支持数组变量的 Bash 中。

未加引号的变量会经历单词拆分和文件名生成 (globbing)。一般来说,你应该总是使用引号,除非你知道需要分词和(可能)通配符。例如,在你的代码中$archive_name应该使用双引号。

$py_exclude我理解您确实希望拆分的内容,因为您希望zip从中获取多个单词( 的参数)。因此您使用了$py_exclude不带引号的 。问题在于通配符。

似乎您想将这些精确的字符串作为单独的参数获取:*.pyc*.ipynb*__pycache__**ipynb_checkpoints*。当每个字符串不带引号或来自不带引号的变量时,都会触发通配符。如果当前工作目录中有匹配的文件(或多个文件),则其名称(它们的名称)将代替相应的模式,并且zip获取后得到的字符串将无法按预期工作。

*__pycache__*我猜你遇到了用 maybe 替换的问题__pycache__,所以你添加了反斜杠来转义星号。但现在这些反斜杠仍然存在!查看此示例:

$    # the current working directory is initially empty
$ variable='foo *__pycache__*'
$ printf '%s\n' "$variable"
foo *__pycache__*
$

好的,但是你想让foo*__pycache__*成为单独的参数。你故意取消对变量的引用:

$    # in the same shell, same directory
$ printf '%s\n' $variable   # deliberately unquoted
foo
*__pycache__*
$

这是您想要的行为。但是文件可能会干扰:

$    # in the same shell, same directory
$ touch __pycache__
$ printf '%s\n' $variable   # deliberately unquoted
foo
__pycache__
$

这是不是您想要什么,所以您尝试逃避星号:

$    # in the same shell, same directory
$ variable='foo \*__pycache__\*'
$ printf '%s\n' $variable   # deliberately unquoted
foo
\*__pycache__\*
$

反斜杠仍然存在。

不仅您对 有问题。如果当前目录中有匹配的文件,您对或或任何其他模式\*__pycache__\*也会有问题。*.pyc*.ipynb

为了解决这个问题,你可以禁用通配符set -f,删除反斜杠,然后仍然使用不带引号的变量:

$    # in the same shell, same directory
$    # the file still exists
$ set -f
$ variable='foo *__pycache__*'
$ printf '%s\n' $variable   # deliberately unquoted
foo
*__pycache__*
$ set +f   # re-enables globbing in case we need it later
$

这样,您就可以从中获取所需的内容$variable,并且结果不依赖于当前工作目录中的文件。然而很难得到foo bar(作为一个单词里面有一个空格) 和*__pycache__*(换句话说)这种方式。一般来说,你想要一个更好的解决方案。

在 Bash 中有一个更好的解决方案正确的事情是使用数组:

$    # in the same shell, same directory
$    # the file still exists
$ set +f   # globbing enabled to show it doesn't break anything
$ variable=("foo bar" '*__pycache__*')
$ printf '%s\n' "${variable[@]}"   # properly quoted
foo bar
*__pycache__*

这是可行的方法。使用数组:

py_exclude=('*.pyc' '*.ipynb' '*__pycache__*' '*ipynb_checkpoints*')

然后使用 获取其所有元素作为单独的单词"${py_exclude[@]}"

答案2

ipynb_checkpoints 带有 _,而不是 -

你有没有尝试过'*.pyc *.ipynb *__pycache__* *ipynb_checkpoints*'

相关内容