为什么

为什么

我有一些文件的名称如下:以“标题”开头。以“.txt”结尾,它们之间可能有其他内容,也可能没有。例如:title.txttitle.abc.txttitle.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/

您可以使用eglob 限定符在 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”,或者简单地说:一个字符cd 零次或一次,在基本 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_GLOBset -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/
  '

相关内容