用grep和正则expression式打印文件

我有一个目录中的许多MP3文件,以不同的数字和空格组合作为文件名。 当我运行ls | grep ^[0-9][0-9]" -" 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 i in ``ls | grep ^[0-9][0-9]" -"``;do echo $i; done我做for循环时也是这样 for i in ``ls | grep ^[0-9][0-9]" -"``;do echo $i; done 我得到了相同的输出,但格式不同

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

为什么这样呢? 我如何即兴创作? 我想要做的是取代01 - Eagles -

在另一个答案中提到了这个问题,但是这里的文字少得多:

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

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

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' 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 globs选择文件来运行prename ,所以你的实际模式不必避免匹配文件名,你可以通过其他的方式进行过滤。 如果你想在模式中使用/ (例如将文件移动到子目录),我建议使用s{pat}{repl}语法。 比一个sed变得更好,变成了\/的森林。

有一个shopt -s extglob bash选项 ,但是除非使用正则表达式,否则最好使用正则表达式,除非您正在编写最高效率的shell脚本。 (例如,每次有人按下tab都会运行的可编程完成功能)。

在处理循环时,包含空格的文件名会导致问题。 你可以通过几种方式处理这个问题:

  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变量只包含一个换行符或类似的东西。 要了解更多关于这个,你可以阅读这篇文章

编辑:尝试awk只有做法:

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

也许我错了,但是你猜想你打算和他们做一些重命名或其他的事情。

如果你使用| xargs之后的“ls |” 这可能会帮助你正确的参数。 但是,即使在这里,我也会建议使用“ls -1”选项(是的,这是一个数字1,每个目录/文件列在1列中)。更好地了解从命令的角度来看另一个文件名从哪里开始)

为了一次性读取整行代码到一个循环中,您已经提到了选项:改变IFS变量,然后一旦完成,就将其更改回来。 或者还有另一种处理空间的优雅方式:

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

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

 ➜ 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 

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

 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