我有一些文件的名称如下:以“标题”开头。以“.txt”结尾,它们之间可能有其他内容,也可能没有。例如:title.txt
,title.abc.txt
,title.123.txt
。
现在我想将它们全部复制到另一个文件夹。如何使用一条cp
命令复制所有文件?
使用正则表达式,可以这样做:
ls | egrep 'title(\..*)*\.txt' | xargs -I{} cp {} destination_folder/
但这太复杂了:您需要egrep
和 的帮助xargs
。有没有一种简单的方法可以使用 shell 的内置函数 glob/wildcards 来实现魔法?就像是:
cp title[magic]txt destination_folder/
答案1
标准 shell 通配符中没有等效项,但某些 shell 有一些扩展:
桀骜
在 zsh 中,这是#
运算符,通过以下选项启用extendedglob
:
set -o extendedglob # best in ~/.zshrc
cp -- *title(.*)#.txt* dest/
您可以使用e
glob 限定符在 glob 中使用 ERE 或 PCRE:
cp -- *(e['[[ REPLY =~ "title(\..*)*\.txt" ]]']) dest/
(set -o rematchpcre
使用 PCRE 而不是 ERE,或者使用after-pcre-match
代替的问题)=~
zmodload zsh/pcre
克什
在 ksh(所有版本)中,x*
是*(x)
:
cp -- *title*(.*).txt* dest/
巴什
bash 通过以下选项支持 ksh 运算extglob
符:
shopt -s extglob
cp -- *title*(.*).txt* dest/
克什93
ksh93
可以在其 glob 中使用正则表达式:
cp -- *~(E:title(\..*)*\.txt)* dest/
(E
为了乙此处扩展了正则表达式)。
它还可以使用以下命令将 ERE 转换为全局模式:
$ printf '%P\n' 'title(\..*)*\.txt'
*title*(\.*)\.txt*
(不过这些反斜杠在这里是多余的)
答案2
在基本通配中匹配特定的相同正则表达式的唯一方法是:
cp title[.]txt title.*.txt destination_folder/
假设文件存在(或者设置 nullglob 然后根本没有文件将生成 cp 错误)。
或者:
$ ksh -c '
set -- ~(N)title[.]txt ~(N)title.*.txt
[ $# -ge 1 ] && cp "$@" dir/
'
或者:
$ bash -O nullglob -c '
set -- title[.]txt title.*.txt
[ $# -ge 1 ] && cp "$@" dir/
'
或者在其他 shell 中类似。
为什么
*
从技术上讲, glob 中不存在任何可能与正则表达式等效的东西。
主要是因为正则表达式单*
没有意义。当它前面有东西时它就有意义,然后它意味着重复前一个标记(某物)0
或更多次。
完全等价的是: 的 regex.*
完全等价于 的 glob *
。
类似正则表达式的等价物title(\..*)*\.txt
接近于title.*.txt
.
开始和结束
我假设您应该为 grep 编写的确切正则表达式是:
^title(\..*)*\.txt$
这将符合您的陈述以“标题”开头。和以“.txt”结尾。在正则表达式中,开始应该用 表示^
,结束也应该用 表示。$
默认情况下,开头和结尾包含在 glob 中。
点
点是正则表达式中的特殊字符,应该转义:\.
。
点在全局中并不特殊,不需要转义。
可选字符
但是没有办法在 glob(基本)中表达可选字符。
A (c|d)?
,在正则表达式中表示:“可选的 c 或 d”,或者简单地说:一个字符c
或d
零次或一次,在基本 glob 中没有等效项。为此,您需要使用扩展通配符。可选字符串的语法是:
?(c|d) # glob
所以:
?(.*) # zero or once `?( )` a dot followed by anything (`*`).
含义:可选地匹配一个点,后跟任何内容(甚至是空的)。
整个全局将是:
title?(.*).txt # only in extended globbing.
设置扩展通配符的方法因 shell 而异(不可移植):
ksh : on by default, no need to set anything.
bash : shopt -s extglob
zsh : set -o extendedglob
便携式(几乎)
在基本通配中匹配特定的相同正则表达式的唯一方法是:
echo title[.]txt title.*.txt
空球
但上述所有匹配都假设某些匹配将是扩展全局的结果。如果 glob 可能找不到匹配项(例如title[.]txt
找不到title.txt
文件),那么您还应该设置 nullglob shell 选项来删除 glob(而不是将其保留在命令行中)。
但这也是不可移植的:
- ksh :没有 nullglob 选项。仅适用于使用
echo ~(N)title?(.*).txt
- 巴什:
shopt -s nullglob
。 - 兹什:
set -o NULL_GLOB
或set -G
另外,如果设置了 nullglob 并且没有匹配模式,cp
将生成错误,因为 cp 将“看到”此命令行:
cp dir/
没有给出来源的地方。
失败全局
failglob 也会产生类似的问题。如果多个模式中的任何一个不匹配,则整个命令将失败。
cp title[.]txt title?(.*).txt dir/
如果两种模式中任何一个失败,都会失败。
最终的
即使根本没有匹配并且 cp 没有出现错误,使 cp 工作的唯一方法是:
$ bash -O nullglob -c '
set -- title[.]txt title.*.txt
[ $# -ge 1 ] && cp "$@" dir/
'
$ ksh -c '
set -- ~(N)title[.]txt ~(N)title.*.txt
[ $# -ge 1 ] && cp "$@" dir/
'