在bash中迭代来自管道的文件名

时间:2014-09-26 23:36:32

标签: linux bash for-loop pipe subshell

考虑到我很沮丧...我花了过去2个小时试图弄清楚如何让一个有管道的命令将输出泵送到for循环。关于我正在尝试遵循我的代码的快速故事。

我多年来一直在使用xbmc。然而,在我开始之后不久,我已经导出了我的库,结果证明它比它的价值更麻烦(特别是现在我正在使用包含在其中的文件夹和文件的集合命名方案)。我想删除xbmc添加的所有文件,所以我想我会编写一个脚本来删除所有必需的文件。然而,这就是我遇到问题的地方。

我正在尝试使用locate命令(因为它的速度),然后是grep(去除所有文件系统.tbn)和egrep(删除结果中创建的.actors文件夹xbmc),然后进行排序(虽然排序没有必要,我在调试期间添加了它,因此测试时的输出更好)。问题是只处理了第一个文件然后什么都没有。我在线阅读了很多,并发现bash为每个管道创建了一个新的子shell,当它完成一次循环时,变量现在已经死了。所以我做了更多关于如何解决这个问题,并且所有内容似乎都显示了我如何解决while循环问题,但没有任何for循环。

虽然我觉得自己能够胜任脚本编写,但我总是会遇到这样的问题,证明我还在学习基础知识。任何比我聪明的人的帮助都将不胜感激。

#!/bin/bash

for i in "$(locate tbn | grep Movies | egrep -v .actors | sort -t/ +4)"
do
  DIR=$(echo $i | awk -F'/' '{print "/" $2 "/" $3 "/" $4 "/" $5 "/"}')
  rm -r "$DIR*.tbn" "$DIR*.nfo" "$DIR*.jpg" "$DIR*.txt" "$DIR.actors"
done

阅读下面的回复后,我认为实现我想要的更好的路线如下。我喜欢任何有关新剧本的建议。我不想仅仅复制和粘贴@Charles Duffy的脚本,而是希望找到正确/最好的方式来做这个学习体验,因为总有一种更好,最好的方法来编写代码。

#!/bin/bash

for i in "*.tbn" "*.nfo" "*.jpg" "*.txt" "*.rar" #(any other desired extensions)
do
  find /share/movies -name "$i" -not -path "/share/movies/.actors" -delete
done

我首先在-not -path部分中删除xbmc从输出中放置在源目录(在本例中为/ share / movies)根目录的.actors文件夹,因此没有缩略图(.tbn)从那里删除,但我希望它们从/ share / movies中包含的任何其他目录中删除(如果它包含在特定的电影文件夹中,我想从.actors文件夹中删除缩略图)。 -delete选项是因为在gnu.org页面中建议-delete比调用/bin/rm更好,因为不需要为rm进程分叉,这样可以提高效率,防止开销。

我很确定我希望引用for行中的项目,因此它是在find命令中使用的文字*.tbn。为了让您了解目录结构,它非常简单。我想删除这些目录中的任何* .tbn * .jpg和* .nfo文件。

/share/movies/movie 1/movie 1.mkv  
/share/movies/movie 1/movie 1.tbn  
/share/movies/movie 1/movie 1.jpg  
/share/movies/movie 1/movie 1.nfo  

/share/movies/movie 2/movie 2.mp4  
/share/movies/movie 2/movie 2.srt  
/share/movies/movie 2/movie 2 (subs).rar  

/share/movies/movie 3/movie 3.avi  
/share/movies/movie 3/movie 3.tbn  
/share/movies/movie 3/movie 3.jpg  
/share/movies/movie 3/movie 3.nfo  
/share/movies/movie 3/.actors/actor 1.tbn  
/share/movies/movie 3/.actors/actor 2.tbn  
/share/movies/movie 3/.actors/actor 3.tbn  

3 个答案:

答案 0 :(得分:2)

这只是一个引用问题。 "$(locate tbn | ...)"是一个单词,因为引号会阻止分词。如果省略引号,它会变成多个单词,但文件路径中的空格将成为问题。

就个人而言,我会使用find -exec条款;它可能会慢locatelocate使用定期更新数据库,因此它会降低速度的准确性),但它会避免这种引用问题。

答案 1 :(得分:2)

通常在脚本中从locate读取文件名是坏消息,除非您的locate命令具有NUL分隔名称的选项(因为除NUL或/以外的每个字符都有效在文件名中,换行实际上在文件名中有效,使得locate的输出不明确)。那说:

#!/bin/bash
# ^^ -- not /bin/sh, since we're using bash-only features here!

while read -u 3 -r i; do
  dir=${i%/*}
  rm -r "$dir/"*".tbn" "$dir/"*".nfo" "$dir/"*".jpg" "$dir/"*".txt" "$dir/.actors"
done 3< <(locate tbn | grep Movies | egrep -v .actors)

注意* 不能如果要扩展它们,如果要扩展它们,即使目录名必须在里面双引号如果有空格&amp; c。在他们的名字。


一般来说,我同意@rici - 使用find是迄今为止更强大的方法,尤其是与GNU扩展-execdir一起使用以防止竞争条件被用于导致命令表现得不好。 (想想恶意用户在脚本运行时用符号链接替换目录到其他地方。)

答案 2 :(得分:1)

您编写的第二个脚本是一个改进。但是,仍有空间做得更好:

#!/bin/bash

exts=( tbn nfo jpg txt rar )

find_args=( )    
for ext in "${exts[@]}"; do
  find_args+=( -name "*.$ext" -o )
done

find /share/movies -name .actors -prune -o \
 '(' "${find_args[@]:0:${#find_args[@]} - 1}" ')' -delete

这将构建如下命令:

find /share/movies -name .actors -prune -o \
  '('    -name '*.tbn' -o -name '*.nfo' -o -name '*.jpg' \
      -o -name '*.txt' -o -name '*.rar' ')' -delete

...因此一次性处理所有扩展。