脚本中写道:
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之后ksh
shopt -s extglob
所以:
shopt -s extglob
rm -f -- !(*.yaml)
将删除当前目录中的所有非隐藏文件,除了名称以.yaml
.
无论如何,全局运算符是不是在引用时被识别为这样,因此使用\
(类似 Bourne 的 shell 中的引用运算符之一)不会有帮助。
shell中的等价物zsh
是:
rm -f -- ^*.yaml
为此,您需要set -o extendedglob
.
(它也支持or!(x)
之后)。emulate ksh
set -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