我正在编写 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*'
?