gawk의 변수를 간격 표현식의 반복 간격 값으로 사용

gawk의 변수를 간격 표현식의 반복 간격 값으로 사용

OS 배포판: Ubuntu 22.04.3 LTS
gawk 버전: GNU Awk 5.1.0, API: 3.0(GNU MPFR 4.1.0, GNU MP 6.2.1)

텍스트 파일이 있는데 가끔씩 한 줄에 A가 표시됩니다. 눈에 띄는 양의 공백과 임의의 텍스트가 이어집니다. 저는 gawk를 사용하여 이 줄을 검색하고 줄의 왼쪽 부분을 수정하고 있습니다.

입력하다:

formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
                                                                           random_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
                                                                           random_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text

예상 출력:

formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
                WORDWRAP                                                   random_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
                WORDWRAP                                                   random_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text

이 명령은 작동하고 예상된 출력을 반환합니다.

gawk '/^[[:space:]]{75}/ { $0 = substr($0,1,15) " WORDWRAP " substr($0,26) }1' input.txt

제가 하고 싶은 것은 간격 표현식의 반복 간격과 substr 함수의 시작 길이 값에 변수를 할당하는 것입니다. 이 값은 입력 파일에 따라 변경될 수 있기 때문입니다.

세 가지 환경 변수를 설정했습니다.

export PH1="75"; export PH2="15"; export PH3="26"

그런 다음 다음 명령을 실행해 보십시오.

gawk -v gph1="${PH1}" -v gph2="${PH2}" -v gph3="${PH3}" '/^[[:space:]]{gph1}/ { $0 = substr($0,1,gph2) " WORDWRAP " substr($0,gph3) }1' input.txt

수정되지 않은 입력만 반환합니다. 반복 간격을 실제 값으로 설정하고 substr 시작 및 길이 값에 변수를 유지하는 경우:

gawk -v gph2="${PH2}" -v gph3="${PH3}" '/^[[:space:]]{75}/ { $0 = substr($0,1,gph2) " WORDWRAP " substr($0,gph3) }1' input.txt

작동하고 예상 출력을 반환합니다.
나는 또한 수정되지 않은 입력을 반환하기 때문에 성공하지 못하고 이것을 시도했습니다.

gawk '/^[[:space:]]{ENVIRON["PH1"]}/ { $0 = substr($0,1,ENVIRON["PH2"]) " WORDWRAP " substr($0,ENVIRON["PH3"]) }1' input.txt

그러나 이는 반복 간격을 실제 값으로 설정할 때 작동합니다.

gawk '/^[[:space:]]{75}/ { $0 = substr($0,1,ENVIRON["PH2"]) " WORDWRAP " substr($0,ENVIRON["PH3"]) }1' input.txt

간격 표현식에서 변수를 반복 간격으로 사용하는 방법이 있습니까?

(2023-09-30 추가) 이 질문에 대한 대답은 '예'이지만 정규식 상수에는 없습니다. 받은 피드백을 바탕으로 명령을 다음과 같이 변경했습니다.

gawk -v gph1="${PH1}" -v gph2="${PH2}" -v gph3="${PH3}" ' $0 ~ "^[[:blank:]]{" gph1 "}" { $0 = substr($0,1,gph2) " WORDWRAP " substr($0,gph3) }1' input.txt

답변1

BEGIN{...}블록에 필요한 정규식을 작성할 수 있습니다 . 예를 들면 다음과 같습니다.

BEGIN { regex = "^[[:space:]]{" gph1 "}" }

그런 다음 기본 스크립트에서 $0입력 줄( )을 정규식과 비교합니다. 예를 들면 다음과 같습니다.

# replace this:

/^[[:space:]]{gph1}/

# with this:

$0 ~ regex

다음 변경 사항을 현재 gawk스크립트에 적용합니다.

gawk -v gph1="${PH1}" -v gph2="${PH2}" -v gph3="${PH3}" '
BEGIN      { regex = "^[[:space:]]{" gph1 "}" }
$0 ~ regex { $0 = substr($0,1,gph2) " WORDWRAP " substr($0,gph3) }
1
' input.txt

그러면 다음이 생성됩니다.

formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
                WORDWRAP                                                   random_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
                WORDWRAP                                                   random_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text

답변2

POSIX awk를 사용하십시오.

$ cat tst.sh
PH1='75'; PH2='15'; PH3='26'

awk -v gph1="$PH1" -v gph2="$PH2" -v gph3="$PH3" '
    $0 ~ "^[[:space:]]{"gph1"}" { $0 = substr($0,1,gph2) " WORDWRAP " substr($0,gph3) }1
' input.txt

$ ./tst.sh input.txt
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
                WORDWRAP                                                   random_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
                WORDWRAP                                                   random_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text

위의 내용은 리터럴(일명 "상수") 정규식 대신 동적(일명 "계산된") 정규식을 사용합니다.https://www.gnu.org/software/gawk/manual/gawk.html#Compulated-Regexps.

하지만 실제로 하드코드를 전달해서는 안 됩니다 gph3. 위치는 대체 텍스트의 길이에 따라 달라지므로 대체 텍스트를 전달하고 gph3원하는 경우 대체 텍스트를 변경할 수 있도록 길이를 기준으로 계산해야 합니다. 그렇게 할 필요 없이 gph3수동으로 다시 계산해야 합니다.

$ cat tst.sh
PH1='75'; PH2='15'; new=' WORDWRAP '

awk -v gph1="$PH1" -v gph2="$PH2" -v new="$new" '
    BEGIN { gph3 = gph2 + length(new) + 1 }
    $0 ~ "^[[:space:]]{"gph1"}" { $0 = substr($0,1,gph2) new substr($0,gph3) }1
' input.txt

하지만 원래 질문으로 돌아가서 ...

동적 정규 표현식을 변수에 저장할 수도 있습니다. 그러면 입력 줄을 읽을 때마다가 아니라 스크립트 실행 시작 시 문자열 연결을 통해 정규 표현식을 한 번만 생성할 수 있습니다.

awk -v gph1="$PH1" -v gph2="$PH2" -v gph3="$PH3" '
    BEGIN { re = "^[[:space:]]{"gph1"}" }
    $0 ~ re { $0 = substr($0,1,gph2) " WORDWRAP " substr($0,gph3) }1
' input.txt

원하는 경우 re스크립트 내 대신 명령줄에서 이 변수를 정의할 수 있습니다.

awk -v re="^[[:space:]]{$PH1}" -v gph2="$PH2" -v gph3="$PH3" '
    $0 ~ re { $0 = substr($0,1,gph2) " WORDWRAP " substr($0,gph3) }1
' input.txt

GNU awk를 사용하면 포함할 변수를 정의할 수도 있습니다.강력한 형식의 정규식 상수그런 다음 사용하십시오.

$ cat tst.sh
PH1="75"; PH2="15"; PH3="26"

gawk -v re="@/^[[:space:]]{$PH1}/" -v gph2="$PH2" -v gph3="$PH3" '
    $0 ~ re { $0 = substr($0,1,gph2) " WORDWRAP " substr($0,gph3) }1
' input.txt

$ ./tst.sh input.txt
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
                WORDWRAP                                                   random_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text
                WORDWRAP                                                   random_text
formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text formatted_text

명령줄이 아닌 스크립트( ) 내에서 동적으로 강력한 형식의 정규식을 생성하려는 경우 re그렇게 할 수 있지만 제 생각에는 구문이 약간 투박해 보입니다. 문자열과 달리 강력한 형식의 정규식은 연결이 없기 때문 입니다. *sub()표현식에 대한 연산자 이지만 문자열과 마찬가지로 연산에 대한 자리 표시자를 x사용할 수 있습니다.sub()

gawk -v gph1="$PH1" -v gph2="$PH2" -v gph3="$PH3" '
    BEGIN { re = @/^[[:space:]]{x}/; sub(/x/,gph1,re) }
    $0 ~ re { $0 = substr($0,1,gph2) " WORDWRAP " substr($0,gph3) }1
' input.txt
gawk -v gph1="$PH1" -v gph2="$PH2" -v gph3="$PH3" '
    BEGIN { re = @/x/; sub(/x/,"^[[:space:]]{"gph1"}",re) }
    $0 ~ re { $0 = substr($0,1,gph2) " WORDWRAP " substr($0,gph3) }1
' input.txt

OP 상황의 경우 일반 동적 정규식에 비해 강력한 형식의 정규식을 사용하면 이점이 없습니다. 옵션이고 다른 상황에서 유용할 수 있기 때문에 여기에 표시하고 있습니다.초보자 매뉴얼.

이 경우 강력한 형식의 정규식의 유일한 작은 이점은 연산자가 내부적으로 \s대신[[:space:]]사용\s

$ echo 'foo bar' | gawk -v re='\s' '$0 ~ re'
gawk: warning: escape sequence `\s' treated as plain `s'

$ echo 'foo bar' | gawk -v re='\\s' '$0 ~ re'
foo bar

$ echo 'foo bar' | gawk -v re='@/\s/' '$0 ~ re'
foo bar

답변3

{repeat}에 대한 상수 정규식에서는 awk var 또는 envvar(또는 기타 비리터럴)을 사용할 수 없지만 첫 번째 var 열만 테스트할 수 있습니다.

substr($0,1,ph1) ~ /^[[:space:]]+$/
# or equivalent but perhaps confusing
substr($0,1,ph1) !~ /[^[:space:]]/
# ENVIRON["PH1"] if you don't make it awk var

그런데 여기서 HT FF VT CR과 같은 문자를 정말 일치시키고 싶나요? 데이터 설명에 따르면 공백이 아닌 실제 공백 문자만 일치시키려는 것 같습니다.캐릭터 클래스여기에는 더 많은 것이 포함됩니다. 그런 이유로

substr($0,1,ph1) == sprintf("%.*s",ph1,"")

아니면 효율성을 위해

BEGIN{ ph1spaces = sprintf("%.*s",ph1,"") } substr($0,1,ph1)==ph1spaces { do change }

또는 동적 정규식을 사용할 수 있지만 매번 다시 컴파일해야 합니다.

$0 ~ sprintf("^[[:space:]]{%d}", ph1)
# or for actual space only
$0 ~ sprintf("^ {%d}", ph1)

비록 gawk만을 요청하고 있지만 이는 gawk에 특정한 것이 아니며 모든 POSIX awk에서 작동해야 합니다.

답변4

perl대신 이것을 사용하면 gawk이식성이 더 좋고, 읽기 쉽고, 안정적이며, 다음을 -i사용하여 파일을 바로 편집할 수 있습니다.

perl -lpse 'substr($_, $offset, length($text)) = $text if /^\s{$spaces}/
  ' -- -offset=14 -text=WORDWRAP -spaces=75 your-file

( perl오프셋은 substr()0부터 시작하므로 15가 아닌 14입니다).


¹ 최신 버전에는 확장 기능이 gawk포함되어 있지만inplace.awk안전하게 사용하기 어렵습니다.

관련 정보