循环遍历当前目录,将目录和子目录名称存储在数组中,不重复

循环遍历当前目录,将目录和子目录名称存储在数组中,不重复

我有一个场景,我循环遍历给定路径中的所有目录和子目录;如果找到具有特定扩展名 (.txt) 的文件,则将目录和子目录的名称存储在数组中。后来,我在这些目录上读取并执行命令。

这是我正在执行的操作:

!/bin/bash
x=( $(find . -name "*.txt") ); echo "${x[@]}"
for item in "${x[@]}"; { echo "$item"; }

我当前的输出是:

./dir1/file1.txt
./dir1/file2.txt
./dir2/subdir1/subdir2/file3.txt

但我想要实现的是,x即使目录包含多个.txt文件,数组中也不应该有任何重复项。此外,我不想将文件名存储为路径;该数组应仅包含目录名称。

预期输出:

./dir1
./dir2/subdir1/subdir2/

答案1

使用bash

shopt -s globstar
shopt -s dotglob nullglob

dirs=( ./**/*.txt )          # glob the names
dirs=( "${dirs[@]%/*}" )     # remove the filenames at the end

这为您提供了可能有重复项的目录路径数组。要删除重复项,请使用关联数组:

declare -A seen
for dirpath in "${dirs[@]}"; do
    seen["$dirpath"]=''
done

dirs=( "${!seen[@]}" )    # extract the keys from the "seen" hash

然后,要打印它们,

printf '%s\n' "${dirs[@]}"

zshshell 中,您可以类似地执行此操作,但使用唯一的数组和 shell 的奇特的全局限定符来删除路径末尾的文件名:

typeset -U dirs

dirs=( ./**/*.txt(DN:h) )

模式后面的通配限定符中的 和 充当 和 中的作用,D即,它们启用隐藏名称的匹配,并在根本没有匹配的情况下删除该模式。最后一个给出了生成的路径名的“头”,即末尾没有文件名的目录路径。Ndotglobnullglobbash:h

shellzsh不必**显式启用使用,因为您必须bash设置globstarshell 选项。

然后,要打印它们,

print -r -C1 -- $dirs

还相关:

答案2

作为一个变体:

#!/bin/bash
x=$(for f in $(find . -name "*.txt"); do echo "${f%/*}"; done | sort -u )
for item in "${x[@]}"; { echo "$item"; }

相关内容