메일 대기열을 주기적으로 정리하는 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`
egrep
grep -E
또한 이는 단일 문자와 일치하는 정규식 연산자이므로 실제로 단일 문자 뒤에 단일 문자 뒤에 가 오는 줄을 .
찾습니다 . grep mms.att.net
예를 들어, 를 포함하는 것과도 일치합니다.mms
att
net
hammstattinet.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
옵션을 사용하여 고정 문자열을 찾을 수 있습니다.grep
fgrep
F
domains=$(<domains.txt tr -d '"' | tr '|' '\n') &&
[ -n "$domains" ] &&
grep -F -- "$domains" file
답변2
@Kaz는 자신의 의견을 작성하여 답변이 허용될 수 있도록 해야 합니다.
피하고 싶다면 eval
코드를 다시 작성하여 넣어야한다고 생각합니다.추가의인용 부호. 내 지나치게 단순화된 규칙은 더 잘 알지 않는 한 모든 달러 기호를 큰따옴표 안에 넣어야 한다는 것입니다.
/etc/mail/email2textdomains.txt
grep이 대안을 표현하는 방법으로 개행을 허용한다는 사실을 활용하기 위해 한 줄에 하나의 필드 로 변경하겠습니다.
mms.att.net
vtxt.com
vtext.com
vzwpix.com
그리고 말하다
domains="$(cat /etc/mail/email2textdomains.txt)"
grep -- "$domains" /var/log/maillog | .... other tasks
따옴표는 내 규칙을 충족시키기 위해 첫 번째 줄에만 있으며 필요하지 않습니다. 이는 텍스트 필드 파일에 선행 문자가 표시되는 것을 --
방지하기 위한 것입니다 . 휴대성을 높이려면 또는 대신 -
직선을 사용하세요 . 사실 너 쓰고 있잖아grep
egrep
grep -E
grep -- "mms.att.net
vtxt.com
vtext.com
vzwpix.com" /var/log/maillog | .... other tasks