bash script (backup)2021. 7. 20. 16:32

 

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

 

문서(txt) 파일에서 중복되는 행(line)을 제거하는 방법은 두가지를 생각해 볼 수 있는데, 하나는 정렬한 후에 제거하는 것이고 나머지 하나는 정렬하지 않고 제거하는 것이다.

행 순서가 바뀌어도 문제가 없는 경우라면 sort 명령을 이용해서 정렬한 후에 중복을 제거하면 된다. -u 옵션을 사용하면 중복된 행이 제거된다. 아래의 예시를 보자.

 

# my_file.txt 내용 확인

cat my_file.txt

def

abc

abc

def

 

# 정렬 및 중복 제거

sort -u my_file.txt

abc

def

 

또는 아래와 같이 사용할 수도 있다. 결과는 동일하다.

 

cat my_file.txt | sort -u

 

그렇다면 행 순서를 바꾸지 않은 상태에서 중복된 행들만 제거하려면 어떻게 할까?

 

심오하고도 위대한 awk 명령을 이용하면 되겠다. 좀 복잡하긴 한데, 일단 아래의 예시를 보자.

 

# 정렬하지 않고 중복 제거

awk '!x[$0]++ {print $0}' my_file.txt

def

abc

 

awk의 기본 동작이 화면에 출력하는 것이기 때문에 {print $0} 부분은 생략해도 무관하다.

 

awk '!x[$0]++' my_file.txt

 

아래과 같이 해도 된다. 결과는 동일.

 

cat my_file.txt | awk '!x[$0]++'

 

'!x[$0]++ {print $0}' 이 표현이 나름 심오한데, awk의 배열(array)이 가진 특징을 생각해 보면 감을 잡을 수 있다. awk에서는 배열의 인덱스(index)가 반드시 0 이상의 정수일 필요가 없고 문자열도 인덱스가 된다. C언어에서는 x[0], x[1] 이렇지만 awk에서는 x[my name is john], x[foo] 이런 것이 가능하다. 게다가 배열의 최대 크기를 지정할 필요도 없다.

 

자, 그러면 위의 표현이 어떤 일을 하는지 생각해 보자. awk에서 행 전체를 지칭하는 변수는 $0이다. 즉, 한 행의 문자열을 x라는 배열의 인덱스로 사용하는 것이다. 차근차근 보면 대강 이렇다.

 

명령을 실행하면 awk는 우선 my_file.txt의 첫 행을 읽어서 배열을 만든다. 해당 변수의 값의 기본 초기값은 0이다. 위의 예시에서 첫 행은 def였다. 즉 x[def]=0 (false)

 

그런데 앞에 ! 기호가 있으니 해당 값의 역(inverse)을 취한다. !x[def]=1 (true)

 

조건값이 non-zero, 즉 0이 아니므로 지정된 명령을 실행한다. {print $0} (문자열 출력)

 

그리고 나서 x[def]++(post-increment)에 의해 x[def]의 값은 1이 된다. x[def]=1

 

my_file.txt의 두번째 행을 읽어서 동일한 절차를 수행한다. 두번째 행은 abc이므로 출력이 끝나고 나면 x[abc]=1

 

이런식으로 가다가 어떤 행을 읽었는데 문자열 def였다고 하자. 즉 중복이 발생한 것이다. 이전에 이미 x[def]=1이므로 !x[def]=0, 즉 조건값이 0(false)이므로 출력되지 않는다. 그리고나서 x[def]++에 따라 x[def]=2 이렇게 된다.

 

이런 원리로 행 순서가 바뀌지 않으면서 중복된 행만 깔끔하게 제거된 결과를 얻을 수 있다. 속도 역시 상당히 빠르다.

 

만약 행 전체가 아니라 첫번째 단어만 기준으로 삼아서 중복을 제거하고 싶다면 x[$0] 대신에 x[$1] 이렇게 바꾸면 된다. (두번째 단어라면 x[$2])

 

awk '!x[$1]++ {print $0}' my_file.txt

 

만약 첫번째 단어만 기준으로 삼아 중복을 제거한 후, 출력은 두번째 단어만 골라서 하고 싶다면 print 부분의 인자를 바꿔주면 되겠다. 아래와 같이 해 보자.

 

awk '!x[$1]++ {print $2}' my_file.txt

 

 

Posted by 반달가면

댓글을 달아 주세요

bash script (backup)2021. 7. 13. 13:35

 

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

 

파일 내용을 출력하기 위해 주로 사용하는 cat 명령은 내용만 출력하고 파일명은 출력하지 않는다. 여러개의 파일 내용을 한꺼번에 확인하고 싶을 때는 파일명을 식별할 수 없기 때문에 불편한 경우가 생길 수 있다.

 

# .txt 확장자인 파일의 내용을 출력, 파일명은 출력되지 않음

cat *.txt

 

파일명을 같이 출력하고 싶다면 cat 명령 대신 head 명령을 사용하자. 여러개의 파일에 대해 적용했을 경우 파일명이 함께 출력된다. 원래 head 명령은 파일의 앞쪽 내용 일부를 출력하기 위한 것이나, 옵션을 조정하면 cat 명령처럼 내용 전체를 출력할 수 있다. 아래의 예시를 참고하자.

 

# myfile.txt에서 맨 앞 5개 행을 출력(-n 옵션값이 양수)

head -n 5 myfile.txt

 

# myfile.txt에서 맨 뒤 3개 행을 제외한 나머지를 출력(-n 옵션 값이 음수)

head -n -3 myfile.txt

 

# myfile.txt 내용 전체를 출력

head -n -0 myfile.txt

 

자, 이제 여러개의 파일에 대해 파일명과 함께 내용을 출력해 보자.

 

# .txt 파일에 대해 파일명과 내용 전체를 출력

head -n -0 *.txt

==> filename1.txt <==

[filename1.txt의 내용]

==> filename2.txt <==

[filename2.txt의 내용]

...

Posted by 반달가면

댓글을 달아 주세요

bash script (backup)2021. 7. 6. 17:32

 

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

 

정규표현(regular expression)을 이용하면 grep이나 sed에서 특정한 길이의 문자열에 대한 작업을 할 수 있다. 아래의 예시를 보자. my_file.txt 파일에서 영문 소문자로만 되고 길이가 5바이트(byte)인 행만 출력하는 경우이다.

 

cat my_file.txt | grep "^[a-z]\{5\}$"

 

중괄호({}) 안에 수자를 넣어서 바로 앞 표현이 얼만큼 반복되는지 지정할 수 있다. 중괄호가 shell에서 처리되는 것을 막고 grep 명령에 온전하게 인자로 전달되도록 역슬래시(\)로 보호해 준다. 중괄호를 이용한 반복 설정은 아래와 같다.

 

{n} 바로 앞의 항목이 정확히 n번 반복되는 경우

{n,} 최소 n번 이상 반복

{,m} 최대 m번 반복

{n,m} 최소 n번 이상, 최대 m번 반복

 

예시를 하나 더 보자. my_file.txt에서 영문 대소문자로 된 5바이트 이상 10바이트 이하 문자열이 있는 행만 출력하는 경우이다.

 

cat my_file.txt | grep "[[:alpha:]]\{5,10\}"

 

이 기능을 이용해서 2개 이상의 문자열이 동시에 포함된 행을 골라낼 수도 있다. 예를 들어 한 행에 abc와 def가 모두 있으면서 abc가 나온 후에 def가 나오는 문자열이 있는 부분을 찾는 경우이다. 아래의 예시를 보자.

 

cat my_file.txt | grep "abc[[:print:]]\{0,\}def"

 

위의 예시에서 grep 명령의 인자를 해석해 보자면, 문자열 abc 이후에 출력 가능한 문자([[:print:]])가 0번 이상(\{0,\}) 나온 후에 def가 나오는 경우를 찾아서 출력하라는 것이다.

 

2개 이상의 문자열이 동시에 포함된 행을 찾는 경우는 grep 명령을 파이프(|)로 연결하여 연속해서 사용할 수도 있다.

 

cat my_file.txt | grep "abc" | grep "def"

 

그러나, -v 옵션을 통해서 2개 이상의 문자열이 동시에 포함된 행을 제외하는 경우는 중괄호를 사용해야 한다.

 

# 검색한 문자열이 포함된 행을 제외(-v 옵션)

cat my_file.txt | grep -v "abc[[:print:]]\{0,\}def"

 

 

 

Posted by 반달가면

댓글을 달아 주세요