shell脚本迭代抛出目录和分割文件名

我需要从文件名中提取2个东西 – 扩展名和数字。

我有一个文件夹“/ var / www / html / MyFolder /”,这个文件夹包含更多的文件夹,并在每个文件夹中存储一些文件。 该文件具有以下结构:“a_X_mytest.jpg”或“a_X_mytest.png”。 “a_”是固定的,并在每个文件夹相同,我需要“X”和文件扩展名。

我的脚本如下所示:

#!/bin/bash for dir in /var/www/html/MyFolder/*/ do dir=${dir%*/} find "/var/www/html/MyFolder/${dir##*/}/a_*.*" -maxdepth 1 -mindepth 1 -type f done 

这只是我的脚本的开始。

我的脚本中有一个错误:

 find: `/var/www/html/MyFolder/first/a_*.*': No such file or directory find: `/var/www/html/MyFolder/sec/a_*.*': No such file or directory find: `/var/www/html/MyFolder/test/a_*.*': No such file or directory 

有人知道错误在哪里吗? 下一步,当上面的行正在工作,是分裂find的文件,并得到这两个部分。

要分裂我会使用这个:

 arrFIRST=(${IN//_/ }) echo ${arrFIRST[1]} arrEXT=(${IN//./ }) echo ${arrEXT[1]} 

有人可以帮我解决我的问题吗?

TL;博士:

您的脚本可以简化为以下内容:

 for file in /var/www/html/MyFolder/*/a_*.*; do [[ -f $file ]] || continue [[ "${file##*/}" =~ _(.*)_.*\.(.*)$ ]] && x=${BASH_REMATCH[1]} ext=${BASH_REMATCH[2]} echo "$x" echo "$ext" done 
  • 在你的情况下 ,一个glob(文件名模式,通配符模式)就足够了 ,因为glob可以在层次结构的层次上多个通配符: /var/www/html/MyFolder/*/a_*.* a_*.* /var/www/html/MyFolder/*/a_*.* _ a_*.*找到匹配a_*.*文件a_*.*在文件夹/var/www/html/MyFolder的( */ )的任何直接子文件夹中。
    你只需要find匹配位于子树不同层次上的文件(但是你也可能需要它来满足更复杂的匹配需求)。
  • [[ -f $file ]] || break [[ -f $file ]] || break保证只有文件被考虑,并且如果找到NO匹配,也有效地退出循环。
  • [[ ... =~ ... ]]使用bash的正则表达式匹配运算符=~ ,从每个匹配文件( ${file##*/} )的文件名部分中提取感兴趣的令牌。
  • 正则表达式匹配的结果存储在保留数组变量"${BASH_REMATCH}" ,第一个元素包含第一个加括号的子表达式( (...) – 又名捕获组)捕获的内容等等。

    • 或者,您可以使用数组read匹配的文件名到其组件中:

       IFS='_.' read -ra tokens <<<"${file##*/}" x="${tokens[0]}" ext="${tokens[@]: -1}" 

至于你为什么试了没有工作

  • find不支持globs作为文件名参数,所以它直接解释"/var/www/html/MyFolder/${dir##*/}/a_*.*"
  • 此外,您必须从文件名模式中将搜索的根文件夹与根文件夹的子树的任何级别进行查找:
    • 根文件夹变成文件名参数
    • 文件名模式通过-name-iname (不区分大小写的匹配)选项传递(总是引用)
    • Ergo: find "/var/www/html/MyFolder/${dir##*/}" -name 'a_*.*' ... – name'a find "/var/www/html/MyFolder/${dir##*/}" -name 'a_*.*' ... ,类似于@konsolebox的答案 。

我不确定所需的复杂性,但也许你想要的是

 find /var/www/html/MyFolder/ -mindepth 2 -maxdepth 2 -type f -name 'a_*.*' 

从而:

 while IFS= read -r FILE; do # Do something with "$FILE"... done < <(exec find /var/www/html/MyFolder/ -mindepth 2 -maxdepth 2 -type f -name 'a_*.*') 

要么

 readarray -t FILES < <(exec find /var/www/html/MyFolder/ -mindepth 2 -maxdepth 2 -type f -name 'a_*.*') for FILE in "${FILES[@]}"; do # Do something with "$FILE"... done