![gawk의 변수를 간격 표현식의 반복 간격 값으로 사용](https://linux55.com/image/225036/gawk%EC%9D%98%20%EB%B3%80%EC%88%98%EB%A5%BC%20%EA%B0%84%EA%B2%A9%20%ED%91%9C%ED%98%84%EC%8B%9D%EC%9D%98%20%EB%B0%98%EB%B3%B5%20%EA%B0%84%EA%B2%A9%20%EA%B0%92%EC%9C%BC%EB%A1%9C%20%EC%82%AC%EC%9A%A9.png)
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
안전하게 사용하기 어렵습니다.