Bash는 변수 내부의 따옴표를 따옴표가 아닌 텍스트로 읽습니까? Bash에 "암시적 참조"가 있습니까?

Bash는 변수 내부의 따옴표를 따옴표가 아닌 텍스트로 읽습니까? Bash에 "암시적 참조"가 있습니까?

메일 대기열을 주기적으로 정리하는 bash 스크립트가 있습니다. 어떤 이유로 인해 우리는 @mms.att.net 및 기타 email2SMS 게이트웨이로 전송되어 9시간 이상 대기열에 있었지만 아직 전달되지 않은 모든 이메일을 삭제하기로 결정했습니다.

간단히 말해서 이 스크립트는 다음을 수행합니다.

domains=`cat /etc/mail/email2textdomains.txt`
egrep $domains /var/log/maillog | .... other tasks

내용은 /etc/mail/email2textdomains.txt바로

"mms.att.net|vtxt.com|vtext.com|vzwpix.com"

따라서 egrep 라인은 다음과 같아야 합니다. 이는 제가 명령줄에 입력한 것과 정확히 같습니다.

egrep "mms.att.net|vtxt.com|vtext.com|vzwpix.com" file | ...

이렇게 실행하면 5개 이상의 단계 명령 파이프라인이 되며, 각 명령은 이전 stdout에서 stdin을 읽습니다. 이것은 분명히 내가 원하는 검색이 아닙니다.

egrep  mms.att.net|vtxt.com|vtext.com|vzwpix.com  file | ...

그러나 런타임 시 두 개의 큰따옴표는 다르게 처리됩니다. 즉, 문자열의 일부가 되므로 본질적으로 다음을 검색합니다.

  • "mms.att.net
  • vtxt.com
  • vtext.com
  • vzwpix.com”

분명히 나는 ​​인용이 어떻게 작동하는지 오해했습니다. 해결책은 포함하는 줄을 변경하여 큰 따옴표를 제거하는 것이었고, 그 결과 작동하지 말아야 할 줄이 작동했지만 작동했습니다.

파이프를 통해 테스트를 시도했는데 od -a인쇄되지 않는 문자가 표시되지 않습니다.

콘텐츠가 /etc/mail/email2textdomains.txt정확히 작동 하도록 작동하는 이유

mms.att.net|vtxt.com|vtext.com|vzwpix.com

작성된 대로 오랫동안 실패하는 파이프라인이 언제 있어야 합니까?

답변1

이런 종류의 작업을 디버깅할 때 유용한 도구는 입니다 set -x. 이를 사용하면 명령이 수행하는 작업을 정확하게 확인할 수 있습니다.

$ set -x
$ domains=$(cat domains.txt)
++ cat domains.txt
+ domains='"mms.att.net|vtxt.com|vtext.com|vzwpix.com"'

보시다시피 $domains인용문이 포함되어 있습니다. 따라서 다음과 함께 사용할 때 grep:

$ grep -E -- "$domains" file
+ grep --color -E -- '"mms.att.net|vtxt.com|vtext.com|vzwpix.com"' file

당신이 원하는 것은 쉘 수준에서 따옴표를 사용하는 것입니다.앞으로데이터는 grep명령에 전달되지만 따옴표는 변수 데이터의 일부이므로 다른 문자처럼 처리됩니다. 가장 간단한 해결책은 파일에서 따옴표를 제거하고 변수만 인용하는 것입니다. 이는 어쨌든 모범 사례입니다.

domains=$(tr -d \" < domains.txt) &&
grep -E -- "$domains" file

그런데 using은 전자가 더 명확하고 더 많은 중첩을 허용하며 더 이상 사용 되지 않으므로 var=$(command)using보다 선호됩니다 .var=`command`egrepgrep -E

또한 이는 단일 문자와 일치하는 정규식 연산자이므로 실제로 단일 문자 뒤에 단일 문자 뒤에 가 오는 줄을 .찾습니다 . grep mms.att.net예를 들어, 를 포함하는 것과도 일치합니다.mmsattnethammstattinet.com

따라서 E이러한 도메인을 포함하는 줄과 일치하는 확장 정규식을 작성하려면 s를 제거해야 할 뿐만 아니라 "도메인 이름에서 정규식 연산자인 모든 문자를 이스케이프해야 합니다. 유효한 도메인 이름의 경우 ..

또한 구현에 따라 빈 정규식에 대해 다르게 동작 grep하지만 대부분은 모든 줄을 보고하므로 특별히 처리해야 할 수도 있습니다.

그래서:

regex=$(
  sed 's/"//g; # remove all "s like with tr
       s/\./\\./g; # substitute .s with \.s
      ' domains.txt
) && 
  [ -n "$regex" ] && # check it's not empty 
  grep -E -- "$regex" file

또는 |s를 개행 문자로 바꾸고 (이전) -F옵션을 사용하여 고정 문자열을 찾을 수 있습니다.grepfgrepF

domains=$(<domains.txt tr -d '"' | tr '|' '\n') &&
  [ -n "$domains" ] &&
  grep -F -- "$domains" file

답변2

@Kaz는 자신의 의견을 작성하여 답변이 허용될 수 있도록 해야 합니다.

피하고 싶다면 eval코드를 다시 작성하여 넣어야한다고 생각합니다.추가의인용 부호. 내 지나치게 단순화된 규칙은 더 잘 알지 않는 한 모든 달러 기호를 큰따옴표 안에 넣어야 한다는 것입니다.

/etc/mail/email2textdomains.txtgrep이 대안을 표현하는 방법으로 개행을 허용한다는 사실을 활용하기 위해 한 줄에 하나의 필드 로 변경하겠습니다.

mms.att.net
vtxt.com
vtext.com
vzwpix.com

그리고 말하다

domains="$(cat /etc/mail/email2textdomains.txt)"
grep -- "$domains" /var/log/maillog | .... other tasks

따옴표는 내 규칙을 충족시키기 위해 첫 번째 줄에만 있으며 필요하지 않습니다. 이는 텍스트 필드 파일에 선행 문자가 표시되는 것을 --방지하기 위한 것입니다 . 휴대성을 높이려면 또는 대신 -직선을 사용하세요 . 사실 너 쓰고 있잖아grepegrepgrep -E

grep -- "mms.att.net
vtxt.com
vtext.com
vzwpix.com" /var/log/maillog | .... other tasks

관련 정보