在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[@]}"
答案 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的回答)
我首先假设您的文件和目录名中没有空格。如果文件和目录名中有空格,请参阅本答案的第二部分。
要将A
,result
和dir
按名称传递给您的函数,您需要使用namerefs(types
或local -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