![파일 이름 그룹 찾기/감지](https://linux55.com/image/198051/%ED%8C%8C%EC%9D%BC%20%EC%9D%B4%EB%A6%84%20%EA%B7%B8%EB%A3%B9%20%EC%B0%BE%EA%B8%B0%2F%EA%B0%90%EC%A7%80.png)
fs에는 다음과 같은 파일이 있습니다.
PREFIX_GROUPNAME_OTHERNAMES[.txt|.*]
예를 들어:
A_ABC_A.txt
A_ABC_B.txt
A_ABC_C.txt
A_XYZ_A.txt
A_XYZ_B.txt
A_XYZ_C.txt
몇 가지 추가 작업을 위해 그룹 이름을 얻고 싶습니다.
$# command i'm looking for
result:
> ABC XYZ
이름 구조는 알지만 그룹 이름은 모릅니다.
아이디어(그러나 매우 비싸 보입니다!(큰 목록에서)):
- 모든 파일 검사
- 이름 분할, 그룹 이름별로 목록 생성
- 그룹으로 돌아가기
find 및 awk 아마도 tr이 솔루션을 찾을 때 찾고 있는 것 같습니다.
편집하다:
이는 고유하지 않은 목록을 제공합니다.
find ./ -iname '*.txt' | xargs -n 1 | cut -d '_' -f 2
> ABC
> ABC
> ABC
> XYZ
> XYZ
> XYZ
답변1
다음은 쉘 문자열 조작과 표준 도구만 사용하여 sort
방지합니다.출력 구문 분석ls
또는find
, 다음과 같은 작업을 수행하지 않는 것이 좋습니다.
for f in *.*; do gr=${f#*_};gr=${gr%_*}; printf "%s\n" "$gr"; done | sort -u
귀하의 경우에는 정확하게 출력되어야 합니다.
ABC
XYZ
설명하다:
- 일치하는 모든 파일 이름을 반복합니다
*.*
(말한 대로 모든 파일 이름을 캡처하려면 "최소 포괄적" 패턴이어야 함). - 쉘 문자열 조작을 사용하면 먼저 첫 번째 항목 이전의 모든 항목을 삭제한
_
다음 두 번째 단계에서는 마지막 항목부터 시작하는 모든 항목을 삭제합니다_
. - 우리는 다음과 같은 방법으로 결과를 출력합니다
printf
. (Stéphane Chazelas가 지적했듯이 쉘에 이 명령이 누락될 가능성은 거의 없습니다.)
최종 출력은 유일한 출력이 아닙니다. 중복을 제거하기 위해 출력을 파이프합니다 sort -u
.
노트당신이 말한 대로 이 패턴과 일치하는 파일이 많으면 for
루프 매개변수 목록이 셸의 내부 제한을 초과할 수 있습니다. 또한 이 방법은 파일 이름의 특수 문자와 관련된 많은 함정을 방지하지만 파일 이름에 개행 문자(많은 파일 시스템에서 파일 이름에 유효한 문자)가 포함된 경우 이 방법이 실패함을 의미합니다 printf
.sort
답변2
그리고 zsh
:
typeset -U groups=( **/*_*_*.*(Ne['REPLY=${${(s[_])REPLY:t}[2]}']) )
typeset -U groups=(...)
: 고유한 멤버가 있는groups
배열 로 정의됨U
**/*_*_*.*
: 파일 이름의 맨 오른쪽, 현재 작업 디렉터리 또는 그 아래에.
s가 1개 이상, 2개 이상이 있습니다._
.
(Ne['code'])
:glob 한정자는 glob을 추가로 한정합니다.N
:N
ulglob: 일치하는 항목이 없으면 비어 있도록 확장합니다.e['code']
각 글로브를 1($REPLY
incode
) 로 확장하여 변환합니다.$REPLY:t
:t
파일의 ail(기본 이름)입니다.${(s[_])var}
: 분할_
(그런 다음 로 두 번째 작업을 수행합니다[2]
).
bash
(GNU 쉘), GNU find
및 GNU를 사용하면 awk
다음과 같은 작업을 수행할 수 있습니다.
readarray -td '' groups < <(
LC_ALL=C find . -name '.?*' -prune -o \
-name '*_*_*.*' -printf '%f\0' |
gawk -v RS='\0' -v ORS='\0' -F _ '!seen[$2]++ {print $2}'
)
이는 처음 두 문자 사이에 어떤 문자 또는 문자가 아닌 문자가 있는지에 대한 가정을 하지 않습니다 _
.
둘 다 숨겨진 파일과 숨겨진 디렉터리의 파일을 건너뜁니다. 이를 포함하려면 D
in 에 glob 한정자를 추가 zsh
하거나 -name '.?*' -prune -o
in 을 제거하세요 find
.
파일 목록이 큰 경우 find
- 기반 파일은 전체 목록을 메모리에 저장하지 않으므로 메모리 친화적입니다. 비슷한 접근 방식을 취할 수 있습니다 zsh
.
typeset -A seen=()
: **/*_*_*.*(Ne['! seen[${${(s[_])REPLY:t}[2]}]='])
groups=( ${(k)seen} )
¹ 이 코드의 종료 상태에 따라 파일이 선택되었는지 여부도 결정되지만 이 코드는 항상 true를 반환합니다.
답변3
답변을 얻는 동안 해결책도 찾았습니다. @AdminBee도 언급했듯이:
find
파일 시스템의 방대한 결과 목록에서 xargs
검색 패턴을 제한할 수 없는 경우(예: "*.txt")를 사용하도록 선택할 수 있습니다.
for f in ./some/path/*.txt; do gr=${f#*_};gr=${gr%_*}; echo "$gr"; done | sort -u
> ABC
> XYZ
find ./ -iname '*.txt' | xargs -n 1 | cut -d '_' -f 2 | sort -u
> ABC
> XYZ