【问题标题】:printing files with grep and regex使用 grep 和 regex 打印文件
【发布时间】:2015-10-24 06:09:49
【问题描述】:

我在一个目录中有很多 mp3 文件,文件名使用不同的数字和空格组合。当我运行ls | grep ^[0-9][0-9]" -" 时,我得到如下输出

01 - Hotel California.mp3
02 - Heartache Tonight.mp3
03 - The Long Run.mp3
04 - One Of These Nights.mp3

但是当我用 for 循环做同样的事情时 for i in ``ls | grep ^[0-9][0-9]" -"``;do echo $i; done 我得到相同的输出但格式不同

01
-
Hotel
California.mp3
02
-
Heartache
Tonight.mp3

为什么会这样?我该如何即兴创作?我想做的是用Eagles -替换01 -

【问题讨论】:

标签: regex linux bash shell grep


【解决方案1】:

这在另一个答案中提到过,但这里的周围文字要少得多:

$ rename 's/^\d\d - /Eagles - /' *.mp3

当比你更有经验的人已经做过,比你干净得多时,不要重新发明轮子。

【讨论】:

    【解决方案2】:

    最后我用下面的命令做到了。感谢所有输入

    for i in [0-9][0-9]' -'*; do 
        mv $i `ls $i | grep ^[0-9][0-9]" -" | \
        sed 's/^[0-9][0-9] - /Eagles - /g'`
    done
    

    【讨论】:

      【解决方案3】:

      请参阅http://mywiki.wooledge.org/BashFAQ/001 了解您做错了什么。您必须小心使用带有空格的命令的输出,以确保您不会在将被分词的上下文中使用它。

      在您的具体示例中,您实际上并不需要正则表达式。 Shell glob 表达式可以解决问题。

      for i in [0-9][0-9]' -'*; do
          mv "$i" "${i// /_}";  # / at start of pattern = all matches.
      done
      

      或者更有用的是弄乱文件名:

      • prename(基于 perl 的重命名器,打包在 Debian/Ubuntu 的 perl 包中)
      • mmv'foo*.mp3' 'bar#1.mp3'

      因此,您将曲目编号替换为 Eagles 的目标是:

      prename 's/[0-9]+ -/Eagles -/' *.mp3
      

      或者在每个文件名的前面加上Eagles -

      prename 's/^/Eagles - /'  [0-9]*.mp3  # or:
      prename '$_ = "Eagles - " . $_'  [0-9]*.mp3  # you aren't limited to the s// operator, it really does eval as perl code.
      

      请注意如何使用 shell glob 选择要在 prename 上运行的文件,因此您的实际模式不必避免匹配文件名,您可以通过其他方式过滤。如果您想在您的模式中使用/(例如,将文件移动到子目录),我建议使用s{pat}{repl} 语法。比变成\/ 森林的sed 单线好多了。

      有一个shopt -s extglob bash option,但你最好只使用正则表达式而不是那个,除非你正在编写 shell 脚本以获得最大效率。 (例如,每次有人按下 tab 时运行的可编程完成函数)。

      【讨论】:

      • 您的意见确实很有帮助。终于做到了
      【解决方案4】:

      Bash 中的 for 将每个单词解释为循环中的一个“对象”。 为避免这种情况,您必须将命令放在引号中

      ➜  songs  touch "01 - foo"
      ➜  songs  touch "02 - bar"
      ➜  songs  touch "03 - baz"
      ➜  songs  for SONG in "$(ls | grep -E "^[0-9][0-9] -")"; do echo $SONG; done;
      01 - foo
      02 - bar
      03 - baz
      

      而:

      ➜  songs  for SONG in $(ls | grep -E "^[0-9][0-9] -"); do echo $SONG; done;
      01
      
      foo
      02
      
      bar
      03
      
      baz
      

      编辑:适用于 zsh,但不适用于 bash。这里是 bash 版本:

      ~/tmp/songs$ for SONG in $(ls | grep -E "^[0-9][0-9] -"); do echo $SONG; done;
      01
      -
      foo
      02
      -
      bar
      03
      -
      baz
      ~/tmp/songs$ IFS=$(echo -en "\n\b")
      ~/tmp/songs$ for SONG in $(ls | grep -E "^[0-9][0-9] -"); do echo $SONG; done;
      01 - foo
      02 - bar
      03 - baz
      

      【讨论】:

      • 我认为为了简单地将[0-9][0-9] 替换为“Eagles”,awk 是完成这项工作的工具。
      • 它正在打印所有没有换行符的文件,例如xargs
      • 真的。修复了定义循环拆分器的 IFS 变量(参见 google bash ifs 循环)
      【解决方案5】:

      在处理循环时,其中包含空格的文件名会导致问题。 您可以通过以下几种方式处理此问题:

      1. 使用while read

        ls | grep -E "^[0-9][0-9] -"| while read line do mv "$line" new_name done

      2. 使用 find -print0 和 perl: 下面的命令将给出您正在寻找的输出:

        find ./ -print0 | perl -0 -n -e 'print "$_\n" if /[0-9][0-9] -/;'

        您也需要更改文件名的帮助吗?

      3. 将 IFS 变量更改为仅包含换行符或类似内容。要了解更多信息,您可以阅读this article

      【讨论】:

      • 它给了我所有的组合 san@Ub1:/media/san/Media/test/tmp$ find ./ -print0 | xargs -n1 -0 grep [0-9][0-9]" -" grep: ./: Is a directory Binary file ./008 Whitesnake - Here I Go Again.mp3 matches Binary file ./01 Hey You (2011 - Remaster).mp3 matches Binary file ./01 How Deep Is You Love.mp3 matches
      • 对不起,我的大脑很累,它正在寻找内容。我更新了我的答案。看看这个关于使用 perl 进行重命名的链接:perlmonks.org/?node_id=632437
      • @san1512 我可能会选择 while read 选项,因为它旨在一次处理一行数据。
      • 另一个很好的链接说明为什么 for i in $(cmd) 不好:mywiki.wooledge.org/BashFAQ/001
      【解决方案6】:

      也许我错了,但猜你打算对它们进行一些重命名或其他操作。

      如果你在 "ls |" 之后使用 |xargs这可能会帮助您使用正确的参数。 但即使在这里,我也建议使用“ls -1”选项(是的,那里是数字 1,每个目录/文件都列在 1 列中。从命令的角度找出 1 个文件名从哪里开始,另一个文件名从哪里结束要好得多)

      要一次将整行读入循环,您可以使用已经提到的选项:更改 IFS 变量,然后在完成后将其更改回来。 或者还有另一种优雅的空间处理方式:

      ls -1 *.mp3|while read line;do echo $line ;done
      

      【讨论】:

        【解决方案7】:

        编辑:尝试awk 唯一方法:

        ls <music dir> | awk '/^[0-9][0-9] - .+\.mp3/{$1="Eagles"; print $0}'
        

        【讨论】:

        • 谢谢。我得到了与ls |grep ^[0-9][0-9]" -" |sed 's/^[0-9][0-9]\s-\s/Eagles - /g' 相同的输出,但它无助于重命名文件。由于循环没有提供所需的输出
        • 如果您的系统支持 awk,则不需要使用 for 循环。正则表达式是 awk 命令的第一部分,而替换是第二部分。它的默认行为是遍历行,因此它将ls 的每个结果解释为一行。
        猜你喜欢
        • 2010-09-27
        • 2016-11-09
        • 1970-01-01
        • 2013-06-16
        • 1970-01-01
        • 2018-02-13
        • 2012-11-23
        • 1970-01-01
        • 2013-06-27
        相关资源
        最近更新 更多