0

认为我很沮丧......我在过去的 2 个小时里试图弄清楚如何让一个包含管道的命令将输出泵送到一个 for 循环。关于我正在尝试的内容的快速故事,然后是我的代码。

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

我正在尝试使用 locate 命令(因为它的速度),然后是一个 grep(删除所有文件系统 .tbn)和一个 egrep(从结果中删除 xbmc 创建的 .actors 文件夹),然后是一个排序(尽管排序不是必需的,我在调试期间添加了它,因此测试时的输出更好)。问题是只处理第一个文件,然后什么也没有。我在网上阅读了很多内容,发现 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 行中的项目被引用,因此它是*.tbn在 find 命令中使用的文字。为了让您了解目录结构,它非常简单。我想删除这些目录中的任何 *.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  
4

3 回答 3

2

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

就个人而言,我会使用findwith-exec子句;它可能会更慢locatelocate使用定期更新的数据库,因此它会牺牲准确性来换取速度),但它会避免这种引用问题。

于 2014-09-26T23:44:46.807 回答
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)

请注意,如果您希望它们被扩展, *s不能在双引号内,即使目录名称必须在双引号内才能工作,如果它们有空格 &c。以他们的名义。


一般来说,我同意@rici - usingfind是迄今为止更强大的方法,特别是与 GNU 扩展一起使用-execdir以防止使用竞争条件导致您的命令以不良方式运行。(想象一个恶意用户在您的脚本运行时将带有符号链接的目录替换到其他地方)。

于 2014-09-26T23:45:31.247 回答
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

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

于 2014-09-29T16:51:55.917 回答