특정 경로의 모든 디렉터리와 하위 디렉터리를 반복하는 시나리오가 있습니다. 특정 확장명(.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
비슷하게 수행할 수 있지만 고유한 배열과 셸의 멋진 전역 한정자를 사용하여 경로 끝에 있는 파일 이름을 제거합니다.
typeset -U dirs
dirs=( ./**/*.txt(DN:h) )
패턴 뒤에 오는 and in 와일드카드 한정자는 and in 역할을 합니다. D
즉, 숨겨진 이름 일치를 활성화하고 일치 항목이 전혀 없으면 패턴을 제거합니다. 마지막 것은 생성된 경로 이름의 "헤더", 즉 끝에 파일 이름이 없는 디렉터리 경로를 제공합니다.N
dotglob
nullglob
bash
:h
셸 옵션을 설정 해야 하므로 셸을 사용하기 위해 명시적으로 활성화 zsh
할 필요는 없습니다 .**
bash
globstar
그런 다음 인쇄하려면
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"; }