Bash 및 qmake의 변수에 대한 큰따옴표를 이스케이프합니다.

Bash 및 qmake의 변수에 대한 큰따옴표를 이스케이프합니다.

나는 다음 하위 디렉터리에 모든 파일을 빌드하는 것을 목표로 하는 qmake에 대해 다음 메타 프로젝트 파일을 작성했습니다 .pro(파일 이름은 역사적 이유 및 기타 도구 체인으로 인해 폴더 이름과 일치하지 않습니다). 본질적으로 이것은 다음과 같은 것으로 귀결됩니다(프로젝트 특정 항목은 제쳐두고).

THIS_FILE=make-all.pro
TEMPLATE=subdirs
FIND= "find -name \'*.pro\' -printf \'%p\\n\'"
AWK= "awk \'{$1=substr($1,3); print}\'"
RMTHIS= "awk \'!/$$THIS_FILE/\'"
SUBDIRS= $$system($$FIND | $$AWK | $$RMTHIS )

.proBash 스크립트에서 파일이 포함된 폴더 목록을 가져와야 하므로 메서드를 복사하기로 결정했습니다.

#!/bin/bash
FIND="find -name '*.pro' -printf '%h\\n"
AWK="awk '{\$1=substr(\$1,3);printf}'"
SUBDIRS=$($FIND | $AWK)

분명히 이것은 작동하지 않습니다. awk표현에 오류가 있습니다. invalid char ''Bash에서 동일한 줄을 직접 실행하려고 하면 awk큰따옴표를 사용한 경우에만 실제로 작동한다는 것을 알 수 있습니다.

find -name '*.pro' | awk "{\$1=substr(\$1,3);printf}"

문제가 있는 줄을 다음으로 바꾸세요.

AWK='awk "{$1=substr($1,3);printf}" '

수동으로 입력한 명령의 출력과 달리 작업 결과가 제공되지 않으며 스크립트 출력이 비어 있습니다. 확실히

 find -name '*.pro' 

스크립트에서는 현재 폴더에서만 파일을 찾을 수 있지만, bash 명령줄에서는 해당 파일을 하위 폴더에서 찾습니다. 무엇이 잘못되었으며 qmake도 다르게 작동하는 이유는 무엇입니까?

답변1

나는 qmake 구문에 익숙하지 않지만 귀하의 예에서는 셸과 매우 다른 방식으로 따옴표와 변수를 사용합니다. 따라서 동일한 코드를 사용할 수는 없습니다.

http://unix.stackexchange.com/questions/131766/why-does-my-shell-script-choke-on-whitespace-or-other-special-characters귀하가 알아야 할 사항을 다루었으므로 여기에 귀하의 질문과 관련된 내용을 요약하겠습니다.

즉, 단순히 셸 명령을 문자열에 넣을 수는 없습니다. Bash와 같은 쉘은 문자열을 재귀적으로 구문 분석하지 않습니다. 소스 코드를 구문 분석하고 다음과 같이 명령을 작성합니다.목록문자열: 간단한 명령은 명령 이름(실행 파일 경로, 함수 이름 등)과 해당 인수로 구성됩니다.

이와 같은 할당을 사용하면 AWK="awk '{\$1=substr(\$1,3);printf}'"변수가 문자열로 설정됩니다 . 변수를 외부 큰따옴표 AWK로 사용하면 변수 값을 공백으로 구문 분석하여 문자열 목록으로 변환하지만 이를 셸로 구문 분석하지는 않습니다. $AWK코드이므로 이와 같은 문자는 '실제로 매개변수에 포함됩니다. 이는 거의 필요하지 않으므로 셸에서 변수를 사용하는 데 대한 일반적인 조언은 다음과 같습니다.변수 확장 주위에 큰따옴표 추가당신이 그들을 포기해야 한다는 것을 모르고 그것이 무엇을 의미하는지 이해하지 않는 한. (여기서의 답변이 전체 내용을 말해주지는 않는다는 점을 참고하시기 바랍니다.)

Bash에서는 간단한 명령으로 배열을 채울 수 있습니다.

#!/bin/bash
FIND=(find -name '*.pro' -printf '%h\\n')
AWK=(awk '{$1=substr($1,3);print}')
SUBDIRS=$("${FIND[@]}" | "${AWK[@]}") 

그러나 명령을 저장하는 가장 좋은 방법은 함수를 정의하는 것인 경우가 많습니다. 이는 단순한 명령에만 국한되지 않습니다. 이 방법을 사용하면 리디렉션, 변수 할당, 조건 등을 수행할 수 있습니다.

#!/bin/bash
find_pro_files () {
  find -name '*.pro' -printf '%h\\n'
}
truncate_names () {
  awk '{$1=substr($1,3);print}'
}
SUBDIRS=$(find_pro_files | truncate_names)

이 스크립트로 무엇을 하려는지 잘 모르겠지만(특히 코드 조각 사이에서 findawk코드를 변경하는 것을 고려하면) 아마도 더 좋은 방법이 있을 것입니다. 루프를 사용하여 *.pro현재 디렉터리의 하위 디렉터리에 있는 파일을 반복할 수 있습니다.

for pro in */*.pro; do
  subdir="${pro%/*}"
  basename="${pro##*/}"
done

하위 디렉터리를 재귀적으로 순회하려면 bash에서 **/*.pro대신 사용할 수 있지만 */*.pro이 방법은 디렉터리에 대한 심볼릭 링크로도 재귀된다는 점에 유의하세요.

답변2

확장자를 가진 파일이 직접 포함된 디렉토리를 찾기만 하면 .proxargs 및 선택적으로 uniq를 사용할 수 있으며 디렉토리에 여러 개의 pro 파일이 포함되어 있더라도 디렉토리를 한 번만 나열하려는 경우:

find -type f -name "*.pro" | xargs -I{} dirname {} | uniq

상대 경로 대신 절대 경로가 필요한 경우:

find -type f -name "*.pro" | xargs -I{} dirname {} | xargs readlink -f | uniq

귀하의 요구 사항에 따라 awk를 사용하는 솔루션은 다음과 같습니다.

find -type f -name "*.pro" | awk -F/ 'BEGIN { OFS="/" } { $1=""; $NF=""; print $0 }' | cut -c 2- | uniq

이것이 바로 접두사가 없는 상대 경로입니다 ./.

관련 정보