我有一个场景,我循环遍历给定路径中的所有目录和子目录;如果找到具有特定扩展名 (.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[@]}"
在zsh
shell 中,您可以类似地执行此操作,但使用唯一的数组和 shell 的奇特的全局限定符来删除路径末尾的文件名:
typeset -U dirs
dirs=( ./**/*.txt(DN:h) )
模式后面的通配限定符中的 和 充当 和 中的作用,D
即,它们启用隐藏名称的匹配,并在根本没有匹配的情况下删除该模式。最后一个给出了生成的路径名的“头”,即末尾没有文件名的目录路径。N
dotglob
nullglob
bash
:h
shellzsh
不必**
显式启用使用,因为您必须bash
设置globstar
shell 选项。
然后,要打印它们,
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"; }