bash:使用find作为数组提供多种文件类型

时间:2017-12-05 11:00:35

标签: bash directory filtering

在bash函数中,我想列出给定文件夹中与给定文件类型集相对应的所有文件。在伪代码中,我想象的是这样的东西:

getMatchingFiles() {
  output=$1
  directory=$2
  shift 2
  _types_=("$@")

  file_array=find $directory -type f where-name-matches-item-in-_types_

  # do other stuff with $file_array, such as trimming file names to
  # just the basename with no extension

  eval $output="${file_array[@]}"
}

dir=/path/to/folder
types=(ogg mp3)
getMatchingFiles result dir types
echo "${result[@]}"

为了您的娱乐,以下是基于我目前对bash的了解的多种解决方法,我正在使用它来实现此目的。我对函数返回文件数组的方式有疑问:final命令尝试执行每个文件,而不是设置输出参数。

getMatchingFiles() {
  local _output=$1
  local _dir=$2
  shift 2
  local _type=("$@")
  local _files=($_dir/$_type/*)
  local -i ii=${#_files[@]}
  local -a _filetypes
  local _file _regex

  case $_type in
    audio )
      _filetypes=(ogg mp3)
      ;;
    images )
      _filetypes=(jpg png)
      ;;
  esac

  _regex="^.*\.("
  for _filetype in "${_filetypes[@]}"
  do
     _regex+=$_filetype"|"
  done

  _regex=${_regex:0:-1}
  _regex+=")$"

  for (( ; ii-- ; ))
  do
    _file=${_files[$ii]}
    if ! [[ $_file =~ $_regex ]];then
      unset _files[ii]
    fi
  done

  echo "${_files[@]}"

  # eval $_output="${_files[@]}" # tries to execute the files
}

dir=/path/to/parent
getMatchingFiles result $dir audio
echo "${result[@]}"

3 个答案:

答案 0 :(得分:2)

事实上,可以使用nameref(请注意,您需要bash 4.3或更高版本)来引用数组。如果要将find的输出放到由名称指定的数组中,可以像这样引用它:

#!/usr/bin/env bash

getMatchingFiles() {

   local -n output=$1
   local dir=$2
   shift 2
   local types=("$@")
   local ext file
   local -a find_ext

   [[ ${#types[@]} -eq 0 ]] && return 1

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

   unset 'find_ext[0]'
   output=()

   while IFS=  read -r -d $'\0' file; do
      output+=("$file") 
   done < <(find "$dir" -type f \( "${find_ext[@]}" \) -print0)
}

dir=/some/path

getMatchingFiles result "$dir" mp3 txt
printf '%s\n' "${result[@]}"

getMatchingFiles other_result /some/other/path txt
printf '%s\n' "${other_result[@]}"

不要将变量$dir作为参考传递,而是将其作为值传递。您也可以传递文字。

答案 1 :(得分:0)

更新:namerefs确实可以是数组(参见Pesa的回答)

文件和目录名称中没有空格

我首先假设您的文件和目录名中没有空格。如果文件和目录名中有空格,请参阅本答案的第二部分。

要将Aresultdir按名称传递给您的函数,您需要使用namerefs(typeslocal -n,仅限可用在最新版本的bash中。

另一个困难是根据您传递的类型构建find命令,但这不是主要的。模式替换可以做到这一点。总而言之,像这样的事情应该做你想要的事情:

declare -n

文件和目录名称中有空格(但不包含文件后缀)

如果你的文件和目录名中有空格,那么事情会有点困难,因为你必须分配你的数组,使得名称不会被分成单词;执行此操作的一种可能性是使用#!/usr/bin/env bash getMatchingFiles() { local -n output=$1 local -n directory=$2 local -n _types_=$3 local filter filter="${_types_[@]/#/ -o -name *.}" filter="${filter# -o }" output=( $( find "$directory" -type f \( $filter \) ) ) # do other stuff with $output, such as trimming file names to # just the basename with no extension } declare dir declare -a types declare -a result=() dir=/path/to/folder types=(ogg mp3) getMatchingFiles result dir types for f in "${result[@]}"; do echo "$f"; done 作为文件名分隔符,而不是空格,这要归功于\0的{​​{1}}选项和{{1}的-print0选项}}:

find

文件和目录名称中包含空格,即使在文件后缀

中也是如此

嗯,你应该得到你身上发生的事情......仍然可能,但留下来作为锻炼。

答案 2 :(得分:0)

支持原始的,未修改的调用约定,并使用空格或glob字符正确处理扩展:

#!/usr/bin/env bash

getMatchingFiles() {
  declare -g -a "$1=()"
  declare -n gMF_result="$1"  # variables are namespaced to avoid conflicts w/ targets
  declare -n gMF_dir="$2"
  declare -n gMF_types="$3"
  local gMF_args=( -false )   # empty type list not a special case
  local gMF_type gMF_item

  for gMF_type in "${gMF_types[@]}"; do
    gMF_args+=( -o -name "*.$gMF_type" )
  done

  while IFS= read -r -d '' gMF_item; do
    gMF_result+=( "$gMF_item" )
  done < <(find "$gMF_dir" '(' "${gMF_args[@]}" ')' -print0)
}

dir=/path/to/folder
types=(ogg mp3)
getMatchingFiles result dir types