grep, awk 또는 sed를 사용하여 문자열 리터럴까지 줄의 하위 문자열을 얻는 방법은 무엇입니까?

grep, awk 또는 sed를 사용하여 문자열 리터럴까지 줄의 하위 문자열을 얻는 방법은 무엇입니까?

텍스트 파일을 처리하려고 하는데 특정 문자열 리터럴이 줄 끝에 나타나면 이를 생략합니다. 예를 들어:

원천:

ABC 123
DEF, characters I don't want
GHI, these characters are ok

원하는 출력:

ABC 123
DEF
GHI, these characters are ok

이렇게 하면 grep -v ', characters I don't want$'전체 줄이 무시됩니다.

하위 문자열을 awk원하기 때문에 간단한 열을 만들 수 없습니다., these characters are ok

cut구분 기호는 여러 문자( )여야 하므로 구분 기호를 사용하여 분할 할 수 없습니다 , characters I don't want.

Python을 사용하면 매우 간단합니다. 예를 들면 다음과 같습니다.string.split(", characters I don't want", 1)[0]

(여담으로, 이와 같은 복잡한 상황에서 Python이 더 읽기 쉽고 유지 관리가 용이할 때 grep, awk 또는 sed를 사용하는 사용 사례가 실제로 Python보다 더 나은지 궁금합니다.)

답변1

여기서 가장 분명한 것은 다음을 사용하는 것입니다 sed.

<source sed "s/, characters I don't want\$//"

셸에서 이스케이프된 줄 끝에서 문자열을 찾으면 s해당 문자열을 바꿉니다 (나중에 셸에 무언가가 나타날 경우를 대비해 미래의 증거로).$\$$/

해당 문자열 뒤의 항목(있는 경우)을 제거하려면 로 바꾸십시오. \$단 , 사용자 로케일에서 유효한 텍스트가 아니더라도 끝까지 모든 항목이 일치하도록 .*C의 로케일을 변경해야 합니다 ..*

<source LC_ALL=C sed "s/, characters I don't want.*//"

GNU grep또는 호환 버전의 경우 Perl과 유사한 정규식 지원으로 빌드되면 다음과 같을 수 있습니다.

<source LC_ALL=C grep -Po "^.*?(?=(, characters I don't want)?\$)"

또는 해당 문자열 뒤의 모든 내용을 제거합니다(있는 경우).

<source LC_ALL=C grep -Po "^.*?(?=, characters I don't want|\$)"

또는 pcregrep(Perl과 유사한 정규식 지원이 GNU에서 활성화된 경우 grep이는 실제로 샘플 애플리케이션으로 제공되지만 pcregrepGNU 이상의 기능을 갖춘 libpcre를 통해 수행됩니다 grep):

<source pcregrep -o1 "^(.*?)(, characters I don't want)?\$"

또는 해당 문자열 뒤의 모든 내용을 제거합니다(있는 경우).

<source pcregrep -o1 "^(.*?)(, characters I don't want|\$)"

제거하려는 텍스트에 /정규 표현식 연산자(의미 없는 개행이나 명령 인수나 환경 변수에 전달할 수 있는 NUL 문자 제외)를 포함할 수 있고 쉘 변수에 저장된 경우 다음과 같이 할 수 있습니다.아니요이를 사용하면 sed "s/$string\$//"명령 주입 취약점이 생길 수 있습니다.

Perl-grep의 경우 다음을 사용할 수 있습니다.

string='/.*\^$'
<source LC_ALL=C grep -Po "^.*?(?=(\Q$string)?\$)"
<source pcregrep -o1 "^(.*?)(\Q$string\E)?\$"

또는 해당 문자열 뒤의 모든 내용을 제거합니다(있는 경우).

<source LC_ALL=C grep -Po "^.*?(?=\Q$string|\$)"
<source pcregrep -o1 "^(.*?)(\Q$string\E|\$)"

이는 비록 심각한 결과를 가져오지는 않더라도 여전히 $string격리된 s에 대한 질식을 야기합니다 .\Esed

또는 임의의 문자열을 전달하기 위한 메커니즘을 사용하여 옵션이 있는 모드 perl에서 직접 사용할 수 있습니다 (여기서는 대략적인 옵션 전달에 사용되지만 직접 사용할 수도 있습니다 (파이썬에 해당 ). 또는 환경 변수( 연관 배열에 매핑됨)). 문자열 은 정규 표현식에서 인용될 수 있습니다( 여기서는 in이 문제가 되지 않습니다):sed-p-s@ARGVsys.argv%ENV\Q\E$string

<source perl -spe 's/\Q$string\E$//' -- -string="$string"

또는 해당 문자열 뒤의 모든 내용을 제거합니다(있는 경우).

<source perl -spe 's/\Q$string\E.*$//' -- -string="$string"

perl기본적으로 입력은 사용자의 로캘 문자 집합으로 인코딩되지 않고 바이트로 처리되므로 여기서 로캘을 변경할 필요가 없습니다.

대조적으로, 줄 구분 기호는 패턴 공간( sed기본적 $_으로 작동하는 곳 perl)에 포함되며 해당 정규식 연산자는 주제 끝이나 주제 끝의 줄 구분 기호 앞에 일치하므로 처리할 수 있습니다. 구분된 줄과 무제한된 줄.s///$

답변2

awk를 사용하십시오.

$ awk 'n=index($0 RS,", characters I don\047t want" RS){$0=substr($0,1,n-1)} 1' file
ABC 123
DEF
GHI, these characters are ok

이는 리터럴 문자열 비교를 수행하므로 정규 표현식 메타 문자가 포함된 문자열을 다음 입력과 일치시키려고 시도하는 경우에도 작동합니다.

$ cat file2
ABC 123
DEF, .*, .*
GHI, .* ok

예상되는 결과는 다음과 같습니다.

$ awk 'n=index($0 RS,", .*" RS){$0=substr($0,1,n-1)} 1' file2
ABC 123
DEF, .*
GHI, .* ok

정규식 메타 문자에 관심이 없다면 다음을 수행할 수 있습니다.

$ awk '{sub(/, characters I don\047t want$/,"")} 1' file
ABC 123
DEF
GHI, these characters are ok

그러나 예상치 못한 결과가 나타납니다.

$ awk '{sub(/, .*$/,"")} 1' file2
ABC 123
DEF
GHI

그리고 예상되는 출력을 얻으려면 메타 문자를 리터럴로 만들기 위해 이스케이프해야 합니다.

$ awk '{sub(/, \.\*$/,"")} 1' file2
ABC 123
DEF, .*
GHI, .* ok

실제로 원하는 것은 문자 그대로의 문자열 비교뿐이라는 점을 고려하면 이는 다루기 어려워집니다.

바라보다http://awk.freeshell.org/PrintASingleQuote\047대신에 왜 '.

python 대신 awk가 사용되는 이유 - awk는 필수 POSIX 도구이므로 모든 POSIX 호환 Unix 설치에 존재하도록 보장되는 반면, python은 그렇지 않으며 awk로 텍스트를 조작하는 데 일반적으로 awk Python을 사용하는 것보다 훨씬 적은 코드가 필요합니다. . 나는 우리가 어느 쪽이 읽고 유지하기 더 쉬운지에 대해 동의해야 한다고 생각합니다.

답변3

줄 끝의 내용을 미리 알고 있으면 변수 확장과 같은 기능을 지원하는 Bash 및 셸에서 해당 내용을 필터링하는 것이 상당히 쉽습니다. 예를 들어:

#!/usr/bin/env bash
line='DEF, characters I do not want'
echo "${line%, characters I do not want}"

다음을 인쇄합니다:

DEF

이 구문은 끝에서 내용을 제거한 후 문자열의 내용을 반환합니다 ${var%string}. 이 예에서 삭제할 문자열은 " "입니다. 문자열이 끝에 있지 않으면 전체 콘텐츠가 반환됩니다. 변수의 시작 부분에서 문자열을 제거하는 변형과 내용 중간에 있는 문자열을 대체하거나 제거하는 대체 변형이 있습니다.$var%, characters I do not want$line

위의 예에서는 변수에 문자열을 할당할 때 작은따옴표를 사용하여 발생하는 복잡함을 피하기 위해 don't->가 변경되었음을 인정합니다 .do not$line

이 접근 방식의 장점은 스크립트가 간단한 필터링을 수행하기 위해 외부 명령을 호출할 필요가 없다는 것입니다. 하지만 이것이 Python의 강력한 기능을 대체할 수 있을까요?. 아마도 아닐 수도 있지만, 이 작업에 Python 대신 쉘 스크립트를 사용하도록 동기를 부여하는 다른 요인이 있을 수 있습니다.

관련 정보