유닉스 / 리눅스 쉘에서 패턴 일치시 역 또는 음의 와일드 카드를 어떻게 사용할 수 있습니까?


325

이름에 'Music'이라는 단어가 포함 된 파일과 폴더를 제외한 디렉토리의 내용을 복사하려고한다고 가정 해 봅시다.

cp [exclude-matches] *Music* /target_directory

이를 수행하기 위해 [제외 일치] 대신 무엇을해야합니까?

답변:


375

배쉬에서 당신이 가능하게하여 그것을 할 수있는 extglob다음과 같은 옵션을 (교체 ls와 함께 cp물론, 대상 디렉토리를 추가)

~/foobar> shopt extglob
extglob        off
~/foobar> ls
abar  afoo  bbar  bfoo
~/foobar> ls !(b*)
-bash: !: event not found
~/foobar> shopt -s extglob  # Enables extglob
~/foobar> ls !(b*)
abar  afoo
~/foobar> ls !(a*)
bbar  bfoo
~/foobar> ls !(*foo)
abar  bbar

나중에 extglob을 비활성화 할 수 있습니다

shopt -u extglob

14
나는이 기능을 좋아한다 :ls /dir/*/!(base*)
Erick Robertson

6
모든 것을 어떻게 포함 시키고 ( ) 그리고 또한! (b )를 제외 합니까?
Elijah Lynn

4
f제외하고로 시작하는 모든 것을 어떻게 일치시킬 foo까요?
Noldorin

8
왜 이것이 기본적으로 비활성화되어 있습니까?
weberc2

3
shopt -o -u histexpand 느낌표가있는 파일을 찾아야하는 경우 기본적으로 extglob은 해제되어 문서에서 histexpand를 방해하지 않습니다. 문서에서 이것이 왜 그런지 설명합니다. foo를 제외하고 f로 시작하는 모든 항목과 일치 : f! (oo), 물론 'food'는 여전히 일치합니다 ( 'foo'로 시작하는 것을 중지하거나 제거하려면 f! (oo *)가 필요함) ! '.foo'사용 종료 어떤 일의 ( ! .foo) 또는 접두어 myprefix ( .foo)는 (myprefixBLAH 일치하지만 myprefixBLAH.foo)
osirisgothra

227

extglob쉘 옵션은 당신에게 명령 줄에서 더 강력한 패턴 매칭을 제공합니다.

당신은에 그것을 설정 shopt -s extglob하고 그것을 해제 shopt -u extglob.

귀하의 예에서는 처음에 다음을 수행합니다.

$ shopt -s extglob
$ cp !(*Music*) /target_directory

전체 사용 가능한 내선는 종료 글로브의 빙 사업자 (발췌입니다 man bash) :

shopt 내장을 사용하여 extglob 쉘 옵션을 사용하면 여러 확장 패턴 일치 연산자가 인식됩니다. 패턴 목록은 |으로 구분 된 하나 이상의 패턴 목록입니다. 복합 패턴은 다음 하위 패턴 중 하나 이상을 사용하여 형성 될 수 있습니다.

  • ? (pattern-list)
    주어진 패턴이 0 번 또는 1 번 일치합니다.
  • * (pattern-list)
    0 개 이상의 주어진 패턴과 일치
  • + (pattern-list)
    주어진 패턴 중 하나 이상을 찾습니다
  • @ (pattern-list)
    주어진 패턴 중 하나와 일치
  • ! (pattern-list)
    주어진 패턴 중 하나를 제외한 모든 것과 일치

당신이없는 현재 디렉토리에있는 모든 파일 목록을하고 싶어한다면, 예를 들어, .c또는 .h파일을, 당신은 할 것이다 :

$ ls -d !(*@(.c|.h))

물론 일반적인 쉘 글 로빙이 작동하므로 마지막 예제는 다음과 같이 쓸 수도 있습니다.

$ ls -d !(*.[ch])

1
-d의 이유는 무엇입니까?
Big McLargeHuge

2
.c또는 .h파일 중 하나 가 디렉토리 인 경우 @Koveras .
tzot

@DaveKennedy 현재 디렉토리의 모든 것을 나열 D하지만 디렉토리에 포함될 수있는 서브 디렉토리의 컨텐츠는 나열 하지 않습니다 D.
spurra

23

bash에는 없지만 (내가 아는) :

cp `ls | grep -v Music` /target_directory

나는 이것이 당신이 찾고있는 것이 아니라는 것을 알고 있지만, 당신의 모범을 해결할 것입니다.


기본 ls는 한 줄에 여러 파일을 넣을 것이므로 올바른 결과를 얻지 못할 수 있습니다.
Daniel Bungert

10
stdout이 터미널 인 경우에만 해당됩니다. 파이프 라인에서 사용될 때 ls는 한 줄에 하나의 파일 이름을 인쇄합니다.
Adam Rosenfield

ls는 터미널로 출력 할 경우 한 줄에 여러 파일 만 넣습니다. 직접 시도하십시오- "ls | less"는 한 줄에 여러 파일을 갖지 않습니다.
SpoonMeiser

3
공백이 포함 된 파일 이름 (또는 다른 흰색 문자)에는 작동하지 않습니다.
tzot

7

exec 명령을 사용하여 mem 비용을 피하려면 xargs로 더 잘 할 수 있다고 생각합니다. 다음은 더 효율적인 대안이라고 생각합니다.

find foo -type f ! -name '*Music*' -exec cp {} bar \; # new proc for each exec



find . -maxdepth 1 -name '*Music*' -prune -o -print0 | xargs -0 -i cp {} dest/

6

bash에서 대안 shopt -s extglobGLOBIGNORE변수 입니다. 실제로는 좋지 않지만 기억하기가 더 쉽습니다.

원래 포스터가 원하는 것일 수있는 예 :

GLOBIGNORE="*techno*"; cp *Music* /only_good_music/

완료되면 소스 디렉토리에 unset GLOBIGNORE있을 수 있습니다 rm *techno*.


5

꽤 간단한 for루프를 사용할 수도 있습니다 .

for f in `find . -not -name "*Music*"`
do
    cp $f /target/dir
done

1
이것은 재귀 찾기를 수행하는데, 이는 OP가 원하는 것과 다른 동작입니다.
Adam Rosenfield

1
사용 -maxdepth 1비 재귀 위해?
avtomaton

쉘 옵션을 활성화 / 비활성화 할 필요없이 이것이 가장 깨끗한 솔루션이라는 것을 알았습니다. 이 게시물에서 OP에 필요한 결과를 얻으려면 -maxdepth 옵션을 사용하는 것이 좋지만 모두 달성하려는 작업에 따라 다릅니다.
David Lapointe

find사소한 파일 이름을 찾으면 백틱에서 사용하면 불쾌한 방식으로 중단됩니다.
tripleee

5

개인적으로 선호하는 것은 grep과 while 명령을 사용하는 것입니다. 이를 통해 강력하면서도 읽기 쉬운 스크립트를 작성하여 원하는대로 정확하게 작업 할 수 있습니다. 또한 에코 명령을 사용하면 실제 작업을 수행하기 전에 드라 이런을 수행 할 수 있습니다. 예를 들면 다음과 같습니다.

ls | grep -v "Music" | while read filename
do
echo $filename
done

복사 할 파일을 인쇄합니다. 리스트가 올 바르면 다음 단계는 단순히 echo 명령을 다음과 같이 copy 명령으로 바꾸는 것입니다.

ls | grep -v "Music" | while read filename
do
cp "$filename" /target_directory
done

1
파일 이름에 탭, 줄 바꿈, 행에 둘 이상의 공백 또는 백 슬래시가없는 한 작동합니다. 병리학 적 사례이지만 가능성을 알고있는 것이 좋습니다. 에서 bash사용할 수 while IFS='' read -r filename있지만 개행은 여전히 ​​문제입니다. 일반적으로 ls파일을 열거하는 데 사용하지 않는 것이 가장 좋습니다 . 같은 도구 find가 훨씬 더 적합합니다.
Thedward

추가 도구가없는 경우 :for file in *; do case ${file} in (*Music*) ;; (*) cp "${file}" /target_directory ; echo ;; esac; done
Thedward

mywiki.wooledge.org/ParsingLs 에는이를 피해야 하는 여러 가지 추가 이유가 있습니다.
tripleee

5

내가 사용하지 않는 아직 여기에 보지 못한 트릭 extglob, find또는이 grep두 개의 파일 세트로 목록 및 치료하는 것입니다 "DIFF" 그들을 사용하여 comm:

comm -23 <(ls) <(ls *Music*)

commdiff여분의 주름이 없기 때문에 바람직 합니다.

이 반환 세트 1의 모든 요소, ls있다, 없다 세트 2도, ls *Music*. 이를 위해서는 두 세트가 제대로 작동하려면 정렬되어 있어야합니다. ls확장에 문제가 없고 glob 확장이 필요 하지만와 같은 것을 사용하는 경우 find반드시 호출하십시오 sort.

comm -23 <(find . | sort) <(find . | grep -i '.jpg' | sort)

잠재적으로 유용합니다.


1
제외의 이점 중 하나는 처음에 디렉토리를 탐색하지 않는 것입니다. 이 솔루션은 하위 디렉토리의 두 가지 순회를 수행합니다. 하나는 제외와 제외입니다.
마크 스 토스 버그

@MarkStosberg, 아주 좋은 지적입니다. 이 기술의 가장 큰 장점은 실제 파일에서 제외 사항을 읽을 수 있다는 것입니다. 예 :comm -23 <(ls) exclude_these.list
James M. Lay

3

이를위한 하나의 솔루션은 find로 찾을 수 있습니다.

$ mkdir foo bar
$ touch foo/a.txt foo/Music.txt
$ find foo -type f ! -name '*Music*' -exec cp {} bar \;
$ ls bar
a.txt

찾기에는 몇 가지 옵션이 있으며 포함 및 제외에 대해 구체적으로 지정할 수 있습니다.

편집 : Adam은 의견에서 이것이 재귀 적이라고 언급했습니다. find 옵션 mindepth 및 maxdepth가이를 제어하는 ​​데 유용 할 수 있습니다.


이것은 재귀 적 사본을 수행하며 이는 다른 동작입니다. 또한 각 파일에 대해 새로운 프로세스를 생성하므로 많은 파일에 매우 비효율적 일 수 있습니다.
Adam Rosenfield

프로세스 생성 비용은 각 파일을 복사하는 모든 IO에 비해 거의 0입니다. 그래서 이것은 가끔 사용하기에 충분하다고 말하고 싶습니다.
21:29에

프로세스 산란에 대한 몇 가지 해결 방법 : stackoverflow.com/questions/186099/...
Vinko Vrsalovic

재귀를 피하려면 "-maxdepth 1"을 사용하십시오.
ejgottl

백틱을 사용하여 쉘 와일드 카드 확장의 아날로그를 가져옵니다. cp find -maxdepth 1 -not -name '*Music*'/ target_directory
ejgottl

2

다음 작품은 *.txt숫자로 시작하는 파일을 제외하고 현재 디렉토리의 모든 파일을 나열합니다 .

이 작품 bash, dash, zsh다른 모든 POSIX 호환 쉘.

for FILE in /some/dir/*.txt; do    # for each *.txt file
    case "${FILE##*/}" in          #   if file basename...
        [0-9]*) continue ;;        #   starts with digit: skip
    esac
    ## otherwise, do stuff with $FILE here
done
  1. 첫 번째 줄에서 패턴 /some/dir/*.txt은 이름이로 끝나는 for모든 파일을 반복합니다 ./some/dir.txt

  2. 두 번째 줄에서 case 문은 원하지 않는 파일을 제거하는 데 사용됩니다. –이 ${FILE##*/}표현식은 파일 이름 (여기 /some/dir/) 에서 선행 dir 이름 구성 요소를 제거하여 패턴이 파일의 기본 이름과 만 일치되도록합니다. (접미사를 기준으로 파일 이름 만 제거하는 경우 $FILE대신 단축 할 수 있습니다 .)

  3. 세 번째 줄에서는 casepattern [0-9]*) 줄과 일치하는 모든 파일을 건너 뜁니다 ( continue명령문은 for루프 의 다음 반복 으로 건너 뜁니다 ). –을 사용하여 문자 (a–z)로 시작하지 않는 모든 파일을 건너 뛰는 것과 같이 여기에서 더 흥미로운 것을 수행 [!a-z]*하거나 여러 패턴을 사용하여 여러 종류의 파일 이름 [0-9]*|*.bak을 건너 뛸 수 있습니다 ( 예 : 두 .bak파일 모두 파일 을 건너 뛰는 경우) 및 숫자로 시작하지 않는 파일.


도! 버그가있었습니다 ( *.txt단지 대신 일치했습니다 *). 지금 수정했습니다.
zrajm

0

이것은 정확히 '음악'을 제외하고 그것을 할 것입니다

cp -a ^'Music' /target

Music? * 또는 *? Music 같은 것을 제외하기위한 것

cp -a ^\*?'complete' /target
cp -a ^'complete'?\* /target

cpMacOS 의 매뉴얼 페이지에는 -a옵션이 있지만 완전히 다른 기능을합니다. 어떤 플랫폼이이를 지원합니까?
tripleee
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.