bash script2023. 7. 28. 18:28

 

반달가면 이글루에서 백업 - http://bahndal.egloos.com/594363

텍스트 파일에서 특정 문자열을 검색해야 하는데, 해당 문자열이 출현했을 경우 바로 이전 행만 출력해야 할 경우에 awk를 이용해서 가능하다. 조건문을 사용해야 하므로 좀 복잡하긴 하지만, 어쨌든 가능.

개인적으로 이 문제에 대한 고민은 여러 행으로 이루어진 텍스트 파일에서 특정 부분을 따옴표(")로 묶어주어야 하는 문제 때문에 시작되었다. 어떤 형태의 문제였는지 예를 들자면 아래와 같다.

john과 jane의 대화를 기록한 my_dialog.txt 파일의 내용이 아래와 같은 상황이다.

# 파일 내용 확인
cat my_dialog.txt
john: hello, jane.
      how are you?
jane: not bad, john. thanks

위의 내용을 아래와 같이 바꿔야 하는 상황이다.

john: "hello, jane.
       how are you?"
jane: "not bad, john. thanks."

즉, 어떤 대화는 여러줄이고 어떤 대화는 한 줄인데 어쨌든 대화의 시작과 끝에 따옴표를 해 주어야 하는 경우였다.

일단 대화의 시작 부분에 따옴표를 추가하는 것은 어렵지 않다. 이름 다음에 콜론(:)이 있으므로 그 다음에 따옴표를 붙이면 된다. 아래와 같이 sed를 이용한 문자열 대체로 가능하다.

 

sed 's/: /: \"/g' my_dialog.txt > my_dialog.tmp
cat my_dialog.tmp
john: "hello, jane.
       how are you?
jane: "not bad, john. thanks.

문제는 뒤쪽 따옴표다. 이것은 awk에서 해결해야 하는데, 현재 행에 콜론이 있을 경우 바로 이전 행에서 말이 끝난 것이므로 이전 행 마지막에 따옴표를 붙여서 출력해 주어야 한다. 현재 행에 콜론이 없다는 것은 이전 행에서 대화가 이어지고 있다는 뜻이므로 이전 행 마지막에 따옴표를 붙이면 안된다. 이것을 awk의 if 조건문으로 표현하면 아래와 같다.

awk '{
  if (/: /) {print var "\""}
  else {print var}
  }
  {var=$0}' my_dialog.tmp
"
john: "hello, jane.
       how are you?"

현재 행에 콜론이 있으면 변수 var의 내용과 함께 따옴표를 출력하고, 그렇지 않으면 변수 var만 출력한다. 출력한 후에 현재 행($0)을 변수 var에 할당.

위의 결과에서 보듯이, 여기까지 하면 두가지 문제가 있다. 이전 행을 출력하기 때문에 첫번째 행에 대해 작업할 때 var에 할당된 내용이 없어서 빈 행에 따옴표만 하나 찍히는 것이 첫번째 문제다. 두번째로 이전 행을 출력하므로 파일의 마지막 행이 출력되지 않는다.

마지막 행 출력은 마무리 부분을 지정하는 END 구분으로 해결하고(마지막 행이므로 끝에 따옴표를 붙여주어야 한다) 첫번째 행의 따옴표는 grep으로 제거하면 되겠다.

awk '{
  if (/: /) {print var "\""}
  else {print var}
  }
  {var=$0}
  END {print $0 "\""}' my_dialog.tmp | grep "^\"$"
john: "hello, jane.
       how are you?"
jane: "not bad, john. thanks."

이렇게 해서 해결.

만약 grep을 사용하지 않고 awk 안에서 다 해결하려면 행번호에 해당되는 내부 변수인 NR의 값이 1일 경우(즉, 첫번째 행을 처리할 때) 행의 내용을 변수 var에 저장한 후 getline 명령으로 다음 행으로 넘어가도록 하면 된다. 따라서 아래와 같은 형태가 될 것이다.

awk '{
  if (NR==1) { var=$0; getline }
  }
  {
  if (/: /) {print var "\""}
  else {print var}
  }
  {var=$0}
  END {print $0 "\""}' my_dialog.tmp

자, 이제 다 되었다. 설명의 편의를 위해 임시 파일 my_dialog.tmp를 생성했지만, 굳이 그럴 필요 없이 곧바로 파이프(|)로 넘기면 된다. 위의 예시에서 sed와 awk의 순서는 바뀌어도 무방하므로 아래와 같이 입력.

awk '{
  if (NR==1) { var=$0; getline }
  }
  {
  if (/: /) {print var "\""}
  else {print var}
  }
  {var=$0}
  END {print $0 "\""}' my_dialog.txt | sed 's/: /: \"/g'
john: "hello, jane.
       how are you?"
jane: "not bad, john. thanks."

728x90
Posted by 반달가면