bash 스크립트에서 rm -v !(*.yaml)을 실행하는 방법은 무엇입니까?

bash 스크립트에서 rm -v !(*.yaml)을 실행하는 방법은 무엇입니까?

스크립트는 다음과 같습니다.

rm -v !\(*.yaml\) ;\

이것은 생산할 것입니다

rm: cannot remove '!(*.yaml)': No such file or directory

하지만 명령줄에서는 잘 작동합니다. 탈출을 위해 다양한 방법을 시도했습니다.

'\!\(\*.yaml\)'
`\!\(\*.yaml\)`
`!\(*.yaml\"\)`
"\!\(\*.yaml\)"

적절한 이스케이프 시퀀스를 알아낼 수 없는 것 같습니다. 이해가 되지 않습니다. 괄호를 제거하는 것이 나의 첫 번째 단계였습니다. 그리고 탈출을 시도했고 !, 그 다음에는 *. 또한 백틱 이스케이프 해제를 사용해 보았지만 "rm 누락된 피연산자" 오류가 발생했습니다. 나는 조금 어리둥절하다. 이미 약 한 시간을 소비했습니다. "yaml 대신 모든 것을 rm"하면 됩니다... 누구든지 버그를 발견하거나 수정 사항을 제안할 수 있습니까?

나는 또한 #!/bin/sh 및 #!/bin/bash를 시도했습니다. 조금이라도 효과가 있지 않을까 싶었습니다.

답변1

!(x)Korn 쉘 글로벌 연산자입니다. 이 연산자를 포함하지만 1 이후에만 지원되는 glob 연산자 bash의 하위 집합입니다 .kshshopt -s extglob

그래서:

shopt -s extglob
rm -f -- !(*.yaml)

이름이 .로 끝나는 파일을 제외하고 현재 디렉터리에서 숨겨지지 않은 모든 파일이 삭제됩니다 .yaml.

아무튼 글로벌 오퍼레이터는아니요인용할 때 그렇게 인식되므로 \(Bourne 쉘의 인용 연산자 중 하나)를 사용하는 것은 도움이 되지 않습니다.

쉘에서 이에 상응하는 것은 다음과 같습니다 zsh:

rm -f -- ^*.yaml

이를 위해서는 set -o extendedglob.

(또는 !(x)이후에도 지원됩니다).emulate kshset -o kshglob

sh확장 기능으로 지원을 sh기반으로 하는 구현과 같은 일부 구현을 찾을 수 있지만 언어에는 동등한 구현이 없습니다.ksh!(x)


¹ 이는 쉘 구문 분석에 영향을 미치므로 shopt명령이 실행되어야 합니다.앞으로이러한 ksh 스프레드 연산자 중 하나를 포함하는 행은 다음과 같습니다.읽다그리고 구문 분석합니다. 예를 들어 에서는 bash -c 'shopt -s extglob; echo !(x)'전체 행이 먼저 구문 분석된 후(extglob이 아직 열려 있지 않음) shopt실행(음, 구문 오류가 없었다면 실행되었을 것임)되기 때문에 구문 오류가 발생합니다. bash -O extglob -c 'echo !(x)'매우 좋은.

답변2

GLOBIGNOREBash에는 glob에서 제거할 파일 이름을 쉘에 알려주고 나머지 파일 이름(이 경우 rm -v) 을 계속 사용 하는 변수라는 기능이 있습니다 .

다음은 데모입니다:

mkdir /tmp/this-test
cd /tmp/this-test
touch one.yaml two.yaml three.json four.txt
echo *

생산:

four.txt one.yaml three.json two.yaml

이제 GLOBIGNORE.yaml 파일 무시를 설정한 다음 해당 효과를 비활성화합니다.

GLOBIGNORE='*.yaml'
echo *
GLOBIGNORE=
echo *

생산:

four.txt three.json
four.txt one.yaml three.json two.yaml

이 명령은 이름이 로 끝나지 않는 디렉토리를 삭제 rm -v *합니다 .echo *.yaml

GLOBIGNORE:각각 콜론( )으로 구분된 glob 표현식 목록이 있을 수 있습니다 . 예를 들어 GLOBIGNORE='*.yaml:*.json' 위 데모에서 이 값을 설정하면 이 GLOBIGNORE값은 glob 목록에서 .yaml 및 .json 파일 이름을 제거하므로 four.txt해당 파일만 표시되거나 제거됩니다.

답변3

find와 rm을 결합하는 또 다른 방법...

5개의 파일이 포함된 폴더로 시작

cd /path/to/files/
touch one.yaml two.yaml three.txt four.yaml five.json

이름 패턴을 제외할 수 있는 find 유틸리티를 사용할 수 있지만, 비재귀적 검색에 find를 사용하려면 -minlength 및 -maxlength를 1로 설정해야 합니다.

find /path/to/files -mindepth 1 -maxdepth 1 -type f ! -name '*.yaml' -exec rm -v '{}' \;
removed '/path/to/files/three.txt'
removed '/path/to/files/five.json'

그런 다음 찾은 각 파일에 대해 개별적으로 rm -v를 사용할 수 있습니다. 파일 수가 많은 경우 각 파일에 대해 개별적으로 rm을 호출하므로 속도가 느려질 수 있습니다.

그러나 -delete를 사용하면 자세한 출력 없이 훨씬 빠르게 동일한 효과를 얻을 수도 있습니다.

find /path/to/files -mindepth 1 -maxdepth 1 -type f ! -name '*.yaml' -delete

관련 정보