如何在 bash 脚本中执行 rm -v !(*.yaml)?

如何在 bash 脚本中执行 rm -v !(*.yaml)?

脚本中写道:

rm -v !\(*.yaml\) ;\

这会产生

rm: cannot remove '!(*.yaml)': No such file or directory

但在命令行中工作正常。尝试过各种方式逃避:

'\!\(\*.yaml\)'
`\!\(\*.yaml\)`
`!\(*.yaml\"\)`
"\!\(\*.yaml\)"

似乎无法找出适当的转义序列,我根本不明白。摆脱括号是我的第一步。然后试图逃跑!,然后*。还尝试使用反引号不转义,但出现错误“rm 缺少操作数”。我有点难住了。已经花了大约一个小时 - 只是“rm 所有内容而不是 yaml”...任何人都可以发现错误/建议修复吗?

我还尝试过#!/bin/sh 和#!/bin/bash。心想也许会有一些效果。

答案1

!(x)是一个 Korn shell 全局运算符。支持 的glob 运算符bash的子集,包括该运算符,但仅在1之后kshshopt -s extglob

所以:

shopt -s extglob
rm -f -- !(*.yaml)

将删除当前目录中的所有非隐藏文件,除了名称以.yaml.

无论如何,全局运算符是不是在引用时被识别为这样,因此使用\(类似 Bourne 的 shell 中的引用运算符之一)不会有帮助。

shell中的等价物zsh是:

rm -f -- ^*.yaml

为此,您需要set -o extendedglob.

(它也支持or!(x)之后)。emulate kshset -o kshglob

sh尽管您可能会发现某些sh实现,例如基于ksh支持!(x)作为扩展的实现,但该语言中没有等效的实现。


¹ 由于这会影响 shell 语法解析,因此该shopt命令必须已执行包含这些 ksh 扩展运算符之一的行是并解析。例如,在 中bash -c 'shopt -s extglob; echo !(x)',您会收到语法错误,因为首先解析整行(extglob 尚未打开),然后shopt运行(好吧,如果没有语法错误,就会运行)。bash -O extglob -c 'echo !(x)'很好。

答案2

Bash 有一个功能,变量GLOBIGNORE可以告诉 shell 从 glob 中删除哪些文件名,而剩下的文件名则继续进行操作(在本例中为rm -v)。

这是一个演示:

mkdir /tmp/this-test
cd /tmp/this-test
touch one.yaml two.yaml three.json four.txt
echo *

生产:

four.txt one.yaml three.json two.yaml

现在设置GLOBIGNORE忽略.yaml文件,然后禁用它的效果:

GLOBIGNORE='*.yaml'
echo *
GLOBIGNORE=
echo *

生产:

four.txt three.json
four.txt one.yaml three.json two.yaml

该命令rm -v *echo *删除目录中名称不以.yaml.

GLOBIGNORE可以有一个 glob 表达式列表,每个表达式用冒号 ( :) 分隔。例如,如果我 GLOBIGNORE='*.yaml:*.json' 在上面的演示中进行了设置,则该GLOBIGNORE值将从 glob 列表中删除 .yaml 和 .json 文件名,因此只会four.txt显示或删除该文件。

答案3

另一种方法结合 find 和 rm ...

从包含五个文件的文件夹开始

cd /path/to/files/
touch one.yaml two.yaml three.txt four.yaml five.json

您可以使用实用程序 find,它允许排除名称模式,但是要使用 find 非递归扫描,您需要将 -mindepth 和 -maxdepth 设置为 1。

find /path/to/files -mindepth 1 -maxdepth 1 -type f ! -name '*.yaml' -exec rm -v '{}' \;
removed '/path/to/files/three.txt'
removed '/path/to/files/five.json'

然后您可以对找到的每个文件单独使用 rm -v 。对于大量文件,这可能会很慢,因为它对每个文件分别调用 rm 。

但是,您也可以使用 -delete 来获得相同的效果,没有详细的输出,但速度会更快。

find /path/to/files -mindepth 1 -maxdepth 1 -type f ! -name '*.yaml' -delete

相关内容