"찾기: 경로는 표현식보다 앞에 있어야 합니다:" 스크립트 내에서 실행할 때만 오류가 발생합니다.

"찾기: 경로는 표현식보다 앞에 있어야 합니다:" 스크립트 내에서 실행할 때만 오류가 발생합니다.

명령줄에서 find 명령을 실행하면 제대로 작동하지만 스크립트에서 실행하면 이 오류가 발생합니다. 다음과 같이 명령을 실행합니다.

FILTER=" | grep -v \"test\""
files=`find . -type f -regex \".*$ext\" $FILTER`

만약 내가한다면

echo "find . -type f -regex \".*$ext\" $FILTER" 

그것은 출력한다

find . -type f -regex ".*.cpp" | grep -v "test"

명령줄에서 잘 작동합니다. 스크립트에서 어떻게 작동하게 할 수 있나요? 또한 *를 탈출하려고 시도했지만 동일한 오류가 발생했습니다.

나도 눈치챘다

find . -type f -regex \".*$ext\"

쉘 스크립트에서 실행할 때 출력이 없습니다. 위와 같이 명령줄에서 실행하면 .cpp 파일 목록이 표시되기 때문에 왜 그런지 잘 모르겠습니다.

답변1

껍질이 도착하면'확장' 단계, 제어 연산자(예 |: )가 식별되었습니다. 확장된 결과는 제어 구조를 검색할 때 다시 구문 분석되지 않습니다.

명령이 다음으로 대체되면

files=`find . -type f -regex \".*$ext\" $FILTER`

확장된 후 Bash는 이를 간단한 명령( find)으로 구문 분석하고 그 뒤에 여러 인수가 옵니다. 그 중 두 개는 확장이 필요합니다. 추적을 켜면 실제 확장 명령을 볼 수 있습니다.

$ set -x
$ find . -type f -regex \".*$ext\" $FILTER
+ find . -type f -regex '".*cpp"' '|' grep -v '"test"'

와 결합하면

$ set -x
$ find . -type f -regex ".*.cpp" | grep -v "test"
+ grep --color=auto -v test
+ find . -type f -regex '.*.cpp'

|첫 번째 경우에는 단일 문자 인수가 사용된다는 것을 분명히 알 수 있습니다 find.

명령 문자열을 동적으로 작성하고 실행하려면 새 구문 분석 단계를 명시적으로 추가해야 합니다. eval방법은 다음과 같습니다.

$ set -x
$ files=$(eval "find . -type f -regex \".*$ext\" $FILTER")
++ eval 'find . -type f -regex ".*cpp"  | grep -v "test"'
+++ find . -type f -regex '.*cpp'
+++ grep --color=auto -v test

그러나 변수를 스크립트로 실행할 때는 확실한 보안상의 이유로 변수의 내용을 제어할 수 있는지 확인하는 것이 중요합니다. 이는 또한 프로그램을 읽고 디버그하기 어렵게 만드는 경향이 있으므로 eval최후의 수단으로만 사용하는 것이 좋습니다.

귀하의 경우 더 나은 접근 방식은 다음과 같습니다.

filter=( -regex ".*$ext" '!' -name "*test*" )
find . -type f "${filter[@]}" -exec bash -c '
  # The part of your script that works with "files" goes here
  # "$@" holds a batch of file names
  ' mybash {} +

find의 유연성을 활용 하고 개행 문자가 포함된 파일 이름을 올바르게 처리합니다. 일반적으로 말하면 이는 Bash(버전 4.4 이후)를 사용하고 지원하지 find않는 한 의 출력을 변수에 저장할 수 없게 만드는 극단적인 경우입니다. 비표준 구현 ). 이에 대해 더 자세히 읽을 수 있습니다.mapfile -d '' files < <(find ... -print0)find-print0찾기 결과를 반복하는 것이 왜 나쁜 습관입니까?find, 파이프의 출력과도 관련이 있습니다.

filter배열의 요소 로 인해 임의의 코드가 실행될 수 있으므로(생각해 보세요 filter=( -exec something_evil ';' )) 해당 내용을 제어할 수 있는지 확인해야 합니다.

답변2

FILTER=" | grep -v \"test\""

연산자가 |확장의 결과인 경우 특별한 의미가 없습니다. 따옴표도 마찬가지입니다.

필터를 유지하고 더 쉽게 참조하는 함수를 만듭니다.

filter() {
    grep -v "test"
}

files=$(find . -type f -regex ".*$ext" | filter)

이와 같은 명령 대체를 사용하면 find출력이 단일 문자열로 압축되므로 파일 이름에 공백이 있으면 문제가 발생할 수 있습니다.


필터를 변수 부분에 넣으면 그 이유는 때때로아니요필터가 있으면 함수를 다시 정의하여 빈 필터로 바꿀 수 있습니다.

filter() {
    cat
}

아니면 두 개의 함수를 만들고 변수를 사용하여 어떤 함수를 호출할지 결정하세요.

filter() {
    grep -v "test"
}
filter_null() {
    cat
}
filter_used=filter
files=$(find . -type f -regex ".*$ext" | $filter_used)

관련 정보