大括号(花括号)内的正则表达式——GNU Parallel / xargs / find

4

我在使用GNU parallel (http://www.gnu.org/software/parallel/)时遇到了花括号(大括号)的问题。

我有一个包含四个文件的列表:

file1.txt.super
file2.txt.super
file3.txt.super
file4.txt.super

如果我运行:ls * | parallel "mkdir ./{.}" 我会得到四个目录:
file1.txt
file2.txt
file3.txt
file4.txt

我的问题是,如何简单地返回四个目录,它们分别被称为:
file1
file2
file3
file4

我已经阅读了http://www.linuxjournal.com/article/8919,但是我无法在GNU Parallel中实现这些正则表达式。我觉得我在这里缺少了一些东西。此外,如果有更复杂的正则表达式示例,将不胜感激。


我没有安装并行处理程序,因此无法测试或阅读文档。parallel mkdir file{1..4}可能是我的猜测。 - user unknown
感谢所有抽出时间写/回复的人。这里肯定有一些优秀的答案,我相信这些都会对其他学习 gnu parallel 的人非常有帮助。干杯! - Steve
6个回答

6
是的,看起来你在这里漏了些东西。这篇Linux Journal文章解释了Shell参数扩展功能。那些花括号(总是紧跟着$)与parallel实用程序的默认替换字符串无关,巧合的是,它们也使用花括号。 Parallel文档显示命令行选项允许使用任意字符串代替其括号包含的默认值。
例如,在你的例子中,替换字符串{.}可以更改为%foo
ls * | parallel --extensionreplace %foo "mkdir ./%foo"

关于${...}的更多信息可以在linuxjournal文章中找到,也可以在参数扩展部分的man bash页面中找到。

既然您在评论中询问@AdamLiss的答案,这里有一种利用花括号和--colsep参数执行任务的方法:

ls * | parallel --colsep '\.' "mkdir ./{1}"

注意:这个--colsep 技巧(就像@AdamLiss提出的sed)如果文件名包含超过两个句点,则会产生不良结果(因为路径名在第一个句点处被截断)。
然而,由于--colsep参数是一个正则表达式,所以应该对文件名中的其他句点具有弹性。
ls * | parallel --colsep '\.[^\.]*$' "mkdir ./{1.}"

注意:由于当前(21120422)版本的parallel存在错误,--extensionreplace无法正常工作。但是,由于parallel是一个Perl脚本,您可以通过更改以下内容来修复它:

    "extensionreplace|er" => \$::opt_U,

to

    "extensionreplace|er=s" => \$::opt_U,

+1 绝对棒极了。正是我所需要的。你使用 --colsep 正则表达式非常清晰和有帮助。谢谢! - Steve
1
今天的答案是:parallel --plus mkdir ./{..} - Ole Tange

3

如果您不介意使用 sed,以下是一种解决方法:

ls * | sed 's/\..*//' | parallel "mkdir ./{}"

+1 这是对 sed 的绝妙运用和完美的解决方法。但是是否有更简洁的方法?是否有更好的方式来(滥用)花括号? - Steve

2
这可能适用于你:
ls * | parallel echo {.} | parallel mkdir {.}

1
除了这里已经给出的很好的答案之外,我相信最简单的解决方案(可能是@Steve正在寻找的)如下所示:
ls * | parallel --plus mkdir {..}

gnu parallel中,{.} 替代字符串的作用是将字符串修剪到最外层的.字符。我相信你可以将字符串修剪到3个.级别。
这意味着{.}只会修剪一级到file1.txt{..}会修剪两级到file1{...}将修剪三级。

1

我不明白在并行运行时,".super"是什么或者它放在哪里,也不理解在parallel命令中使用 "{.}"的目的。据我所知,parallel的工作方式类似于xargs,并且只能理解{}。我错过了什么吗?

无论如何,我会用for循环来实现目标:

for f in *; do mkdir "${f%%.*}"; done

或者,如果你真的关心这些短作业的并行性:

for f in *; do mkdir "${f%%.*}" & done

2
你缺少关于“parallel”的文档:http://www.gnu.org/software/parallel/man.html - Adam Liss
嗯,我系统上的版本似乎不同;本地手册页中没有提到 {.}。感谢您为我搜索,@AdamLiss。 - Mark Reed
+1,循环的运用很好,让它并行。我有点怀疑gnu parallel只能理解“{}”和“{.}”,有人能证实一下吗? - Steve
根据AdamLiss上面提供的文档,GNU parallel可以理解各种与{}相关的表达式,但就路径名修改而言,似乎只能删除前导目录和/或单个尾部扩展名。 - Mark Reed

1
根据手册页面中的示例,即使它不太美观,以下内容应该可以工作:
ls *.txt.super| parallel --er {txt} 'echo {txt}|parallel "mkdir ./{.}"'

通过从并行调用并行并将{.}字符串别名设置为并行的父实例中的{txt},可以删除第二个文件扩展名。


我明白你在这里想做什么,但是我得到了返回值:/bin/bash: {txt}: command not found - Steve
对我来说也不起作用(并行似乎无法识别“--er”选项,并将“{txt}”解释为命令),但根据最新的手册页面,这应该是它应该工作的方式。可能是并行中的一个错误。 - Daniel Roethlisberger
@DanielRoethlisberger 您是正确的。--er 不起作用是由于 parallel 中的一个错误。请参见我的答案以获取修复方法。 - Brian Swift

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接