본문 바로가기
Computer/Linux

[리눅스 기초] 3장. 텍스트 다루기 (feat. IO redirection, 파이프)

by injeolmialmond 2021. 7. 25.

이 글은 리눅스를 비롯해 컴퓨터 공학을 처음 접하는 비전공자를 위해 쉽게 풀어쓴 글입니다. 제가 처음 리눅스를 배울 때를 생각하며

작성한 것이기도 합니다. 비전공자이기 때문에 적합하지 않은 표현이 있을 수 있습니다. 실수가 있다면 지적 부탁드립니다.


지난 게시물에서는 리눅스의 기본 명령어 (도움말, 디렉토리, 파일, 압축하기), 파일권한과 환경변수에 대해 알아보았습니다. 이번 게시물에서는 텍스트 파일을 다루는 명령어들에 대해 설명드리겠습니다.

 

 

1. 파일 다운로드

텍스트를 다루기 위해서는 텍스트 파일이 있어야 합니다. 텍스트 파일을 직접 GUI의 메모장으로 작성할 수도 있겠지만 웹에서 어느 정도 길이가 되는 파일을 다운로드 받아보도록 하겠습니다.

1.1 wget

wget URL주소

웹 상의 자료를 다운로드받고 싶을 때는 wget 명령어 뒤에 URL 주소를 인자로 넣어주면 됩니다.

저는 무료 e-book을 제공하는 웹사이트인 http://www.gutenberg.org 에서 하나의 파일을 선택했습니다.

이 때 파일은 현재 디렉토리에 다운로드됩니다. 다운로드 될 때는 위의 콘솔 창처럼 다운로드 되는 단계가 제시됩니다. 다운로드가 완료되기 전까지는 커서가 나타나지 않으므로, 다운로드가 완료될 때까지 기다렸다가 커서가 나타나면 그 때 코드를 작성하면 됩니다.

 

참고로 URL 주소를 잘못 입력했거나, 예전에는 유효했지만 더 이상 유효하지 않은 URL을 입력하면 다음과 같이 

'HTTP request sent, awaiting response... 404 Not Found' 라는 메세지가 뜹니다

 

정상적으로 다운로드되는 경우에는 

'HTTP request sent, awaiting response... 200 OK' 라는 메세지가 뜹니다.

 

 

2. 텍스트 파일 읽기

2.1 cat / tac

2.1.1 cat

cat 파일.txt

cat 뒤에 파일 이름을 적고 엔터를 누르면 다음과 같이 기존의 콘솔 창 안에 해당 텍스트 파일의 내용이 출력됩니다.

만약 여러 개의 파일을 한 번에 출력하고 싶다면, 해당 파일들을 파일1, 파일2, 파일3이라고 할 때

cat 파일1.txt 파일2.txt 파일3.txt

이런 식으로 입력한 뒤 엔터를 누르면 세 개 텍스트 파일이 이어붙여서 출력될 것입니다.

 

그런데 만약 화면에 출력하는 대신, 여러 개 파일을 하나로 이어붙여서 저장하고 싶다면 다음과 같이 입력해주면 됩니다.

cat 파일1.txt 파일2.txt 파일3.txt > 최종파일.txt

* 아니 갑자기 이게 뭐지?! 왜 화면에 나오지 않고 파일로 저장되는거지?

리눅스에서 >, < 표시는 IO Redirection (Input Output Redirection, 입출력 방향변경) 을 표시하기 위한 기호입니다.

쉽게 말하면, 입력과 출력의 방향을 바꾼다는 뜻입니다.

 

*참고: IO Redirection

더 상세하게 설명드리겠습니다. 우리가 위에서 쓴 기호는 > 입니다. 이는 출력 방향을 변경한다는 뜻입니다.

>이 없을 때는, 동일한 Commandline Argument(인자: '파일1.txt 파일2.txt 파일3.txt') 을 Unix Process (cat) 이 받아서 Standard Output (콘솔창 출력) 으로 나왔습니다.

> 이 사용되면, 이 출력의 방향을 바꾸어서 '최종파일.txt'라는 파일로 저장하게 되는 것입니다.

 

반대로 < 기호를 사용하면, 입력 방향을 변경하는 것입니다. 다른 명령어에 있어서 사용하게 될 것이니 일단은 입력받는 파일의 앞에 <를 붙인다고만 이해하고 넘어가면 되겠습니다.

 

당장 몇 줄로는 이해하기 어려울 수도 있겠지만, 실습을 하다보면 자연스럽게 어떤 기능인지 캐치할 수 있을 것입니다.

더 상세한 설명은 생활코딩의 관련 강의를 들어보면 되겠습니다.

https://opentutorials.org/course/2598/14199

 

 

2.1.2 tac

tac 파일.txt

tac는 파일의 내용을 행 단위로 뒤집어서 출력합니다.

tac 명령어를 사용한 것을 기준으로, 콘솔 창 속 문자열들이 한 행씩 대칭을 이루는 것을 확인할 수 있습니다.

 

 

2.2 more / less

cat 명령어의 경우 텍스트 전체의 내용이 콘솔 창에 그대로 출력되기 때문에, 엔터를 누른 뒤 확인해보면 출력이 완료된 뒤 뜨는 커서 위에, 출력을 요구했던 텍스트 파일의 꼬리 부분만 몇 줄 볼 수 있었습니다. 긴 텍스트 파일의 내용을 실제로 읽고 싶은 리눅스 사용자라면, cat / tac 명령어가 그닥 좋은 선택지는 아닙니다.

 

대신 more, less의 경우 텍스트의 내용을 순차적으로, 천천히 볼 수 있도록 해줍니다. 그 중에서 more는 더 뒤로 갈 수는 있지만 앞으로 돌아올 수는 없는 명령어입니다. 대신 less는 뒤로도, 앞으로도 이동이 자유롭다. 방향키(위, 아래)를 눌러 이동하고, '/검색어'를 눌러서 원하는 키워드로 검색할 수도 있습니다. 

more 파일.txt
less 파일.txt

더욱 자세한 사용 방법은 less를 실행한 상태에서 h를 누르는 것입니다. 도움말을 나가려면 q를 누르면 됩니다. 텍스트 파일로 돌아올 수 있습니다. 동일하게 less 텍스트 뷰어를 종료하려면 q를 누르면 됩니다. 다시 배시 셸로 돌아올 수 있습니다.

 

2.3 head / tail

more, less 명령어는 내용을 한 페이지씩 볼 수 있도록 하지만, 파일 내용 전체를 불러와야 합니다. 또한 새로운 창을 띄우는 것이 아니라 쉘을 보여주던 기존의 창에 텍스트 뷰어를 띄우는 것이기 때문에 기존 쉘 내용을 보고 싶은 경우 번거롭습니다. 컴퓨터를 사용할 때, 한 번에 단 하나의 창만 띄울 수 있다고 생각해 보세요. 인터넷 브라우저와 텍스트 에디터를 동시에 사용할 수 없을 것입니다. 정말 불편하겠죠? (코딩의 반은 구글링인데..)

이와 달리 head / tail 명령어의 경우 파일의 일부분만을 보여주며, cat, tac 명령어처럼 쉘 안에서 파일의 내용을 출력합니다. 따라서 파일이 어떤 내용을 다루고 있는지 대략적으로 보고자 하는 경우, 데이터의 형식이 어떻게 이루어져 있는지 궁금한 경우에 사용합니다.

head 파일.txt
head -n 숫자 파일.txt

head의 경우 파일의 가장 첫 부분, tail의 경우 가장 뒷 부분을 출력합니다. 또한 아무런 옵션, 인자 없이 명령어를 사용한다면 기본적으로 가장 첫 10행, 가장 마지막 10행을 출력합니다. 출력 행의 개수는 -n 옵션 뒤에 윈하는 숫자를 입력하면 조정할 수 있습니다. 'head -n 20 text.txt'라고 명령을 내리는 경우, 'text.txt의 가장 첫 20줄 출력해줘'라는 뜻입니다.

 

3. 텍스트 통계: wc (word count)

wc 파일.txt

wc 명령은 텍스트 통계를 보여주는 명령어입니다.

 

wc 명령을 실행했을 때, 총 세 개의 숫자가 출력됩니다. 각각 행(줄)의 개수, 단어의 개수, 바이트 수입니다.

여기서 주의할 점이 있습니다.

1. 행의 개수는 실제 행 개수보다 1이 적게 출력됩니다. 이는 줄바꿈 개수를 나타내기 때문이라고 이해할 수 있습니다.

2. 영어는 한 글자당 1바이트, 한국어는 한 글자당 3바이트입니다. 띄어쓰기는 1바이트, 줄바꿈은 2바이트입니다.

'안녕하세요 나는 리눅스 초보입니다' -> 15글자(15*3) + 띄어쓰기 세 번(3) = 48

'Hi I am a beginner of Linux' -> 21글자(21) + 띄어쓰기 여섯 번(6) = 27

 

4. 행 번호 붙이기 : nl

nl 파일.txt

nl 명령어는 행 번호가 텍스트와 함께 출력되도록 합니다.

 

5. 파일 내용 검색: grep

grep "문자열" 파일.txt

grep 명령어는 텍스트 파일에서 어떤 문자열을 검색할 수 있도록 하는 명령어입니다. 검색 문자열이 포함된 행을 모두 출력합니다.

그런데 문제가 있습니다. 나는 "people"이 어떻게 쓰였는지 궁금한데, 이래서는 "people"이 어디 있는지 알아보기가 너무 힘듭니다. 이 때 옵션을 사용할 수 있습니다.

grep --color "문자열" 파일.txt

--color 옵션을 더한 경우, 검색 문자열만 다른 색상으로 표시됩니다.

 

6. 문자 변환 : tr

tr은 사실 파일을 인자로 요구하는 명령어는 아닙니다. 'tr "문자1" "문자2"'의 형태로 입력하고 엔터를 누르면, 다음 줄에 커서가 나타납니다. 컴퓨터가 입력을 요구하는 것입니다. 그 상태에서 아무 문자열이나 입력하면, 해당 문자열 속에 "문자1"이 포함되어 있는 경우, "문자2"로 교체됩니다. 우리는 파일에 있는 문자열을 바꾸고 싶은데, 이 경우 어떻게 하면 될까요? 앞서 배웠던 IO redirection을 이용하면 됩니다.

tr "문자1" "문자2" < 파일.txt

커서로 입력해야 하는 문자열 대신, 원하는 파일을 해당 명령어의 input으로 만드는 것입니다. 이렇게 문자가 변환된 결과는 저장되지 않고, 쉘에만 출력됩니다.

 

7. 텍스트 변환 : sed

tr은 문자 단위로 변환하기 때문에, 문자열을 변환하고 싶은 경우에는 sed라는 명령어를 사용해야 합니다.

sed는 파일명을 인자로 받지 않을 경우 tr과 동일하게 커서가 나타납니다. 하지만 IO redirection 없이 파일명을 인자로 입력할 수도 있습니다. 이 경우 출력만 되고, 저장되지는 않습니다.

sed의 인자는 굉장히 특이한 형태로 나타납니다. 가장 기본적인 형태는 's/문자열1/문자열2/g'입니다. 

sed 's/문자열1/문자열2/g' 파일.txt

사실 sed는 변환으로만 쓰이지는 않습니다. 따옴표 안의 가장 첫 's'는, substitute 라는 뜻으로, 텍스트 변환을 나타냅니다. 그 이후의 '문자열1', '문자열2'는 각각 변환의 대상, 변환의 목표가 되는 문자열입니다. 마지막의 'g'는 global 이라는 뜻으로, 문자열1이 여러번 나타난다면 모두 변환시키라는 뜻입니다.

 


*참고: 파이프

여기서 '파이프' 개념을 한번 설명드리겠습니다. 파이프라고 하면 저는 마리오가 가장 먼저 생각나는데요, 배관공인 마리오는 다른 세계로 이동하기 위해 파이프를 타고 다닙니다.

리눅스에서 파이프도 비슷합니다. 파이프는 한 번에 여러 개의 명령어를 사용할 수 있도록 합니다. 명령어의 결과가 파일에 저장되지 않는 경우가 대부분이기 때문에, 새로 명령어의 결과를 파일로 저장하지 않고도 여러 개의 명령어를 한 번에 쓰는 것은 굉장히 효율적입니다. 명령어1의 출력을 명령어2가 입력으로 받는 것입니다.

명령어1 | 명령어2

예시를 들어서 위에서 봤던 명령어들을 사용해보겠습니다.

'나는 앨리스의 원더랜드에서 앨리스라는 이름은 바바라로 바꾸고, 가장 첫 30줄 중에서 '바바라'라는 이름이 등장하는 행만 보고 싶어. 그리고 그 이름은 색을 바꿔서 표시해줬으면 좋겠어!'

요구사항이 너무 많지만, 한 줄에 이 모든 명령을 실행할 수 있습니다. 일단 이름을 교체하기 위해 sed 명령어를 썼고, 그 출력 결과를 입력으로 받아 head 명령어를 사용합니다. 그 결과 출력되는 30줄을 입력으로 받아서 grep 명령어는 'Barbara'라는 이름만 색을 달리하여 검색해줍니다.


8. 정렬하기 : sort

sort 명령어는 텍스트 파일의 내용을 정렬하여 출력합니다. 먼저 가장 기본적인 형태의 텍스트 파일은 따로 키를 주지 않는다면 각 행을 철자순으로 정렬합니다.

sort 파일.txt

그런데 텍스트 파일 중에서는 어떠한 형식을 따르는 파일들이 있습니다. 엑셀 파일에서 표를 나타내는 직선들이 없는 형태라고 생각하면 쉽습니다.

예시로 텍스트 파일을 만들어보겠습니다.

이름 나이 키

김수안 30 165

박형생 40 170

고민조 45 173

박승진 35 180

이종택 39 176

 

이러한 형태의 텍스트 파일에서 이름, 나이, 키의 경우 필드(변수)이고, 그 아래 각 인물에 대한 기록 한 줄 한 줄을 레코드라고 합니다. 이 파일을 그대로 sort 함수에 넣는다면, '이름, 김수안, 박형생, 고민조, 박승진, 이종택'을 ㄱㄴㄷ순으로 정렬할 것입니다.

대신, 저는 나이를 기준으로 인물들을 정렬하고자 합니다. 그렇다면 어떻게 해야 할까요? 첫 줄은 제외를 시키고, 나이를 기준으로 정렬해야 합니다.

첫 행을 제외하려면 어떻게 해야 할까요? 가장 첫 줄을 제외한다는 것은, 가장 마지막 (길이-1)줄을 택하는 것과 같습니다. tail 명령어를 쓰면 됩니다. 그리고 나서 나이를 기준으로 삼아 정렬합니다. 이 때 sort 함수를 -k 옵션과 함께 사용합니다. R이나 python 등에 익숙하시다면 key 라는 개념이 익숙하실 것입니다. 이 두 명령어를 파이프로 연결하여 사용해보겠습니다.

이처럼 'sort -k 숫자 파일.txt'를 입력하면 숫자에 해당하는 순서의 필드를 기준으로 레코드들이 정렬됩니다.

sort -k 숫자 파일.txt

또, 레코드에 있어서 필드를 구분하는 필드 구분자가 띄어쓰기가 아닌 경우가 있습니다. 만약 필드 구분자가 쉼표라면 다음과 같은 형태일 것입니다.

이름,나이,키

김수안,30,165

박형생,40,170

고민조,45,173

박승진,35,180

이종택,39,176

 

이러한 경우, -t 옵션도 함께 사용하여, 필드 구분자를 지정해줄 수 있습니다.

sort -t"필드구분자" -k 숫자 파일이름.txt

이 기능이 중요한 이유는, 위 파일의 경우에는 이름과 나이, 키를 다루기 때문에 띄어쓰기가 없지만, 국가명(ex. 'United States')이나 문장, 여러 단어로 이루어진 문자열 등이 레코드에 포함되어 있을 경우에는 띄어쓰기가 포함되어 있을 수 있기 때문입니다. 

9. 중복행 제거하기: uniq

uniq 명령어는 텍스트에서 중복되는 행을 제거하여 하나만 남도록 만듭니다. 그러나 중복된 행이 인접해있을 경우에만 중복을 제거합니다.

따라서 중복된 것들이 항상 인접하도록 만들어준 뒤에 uniq 명령어를 사용해야 합니다. sort 함수를 사용한 뒤에 uniq 명령어를 사용해주면 됩니다. 또한 개수를 셀 수도 있습니다. count를 뜻하는 옵션 -c를 붙여주면 됩니다.

 

 

댓글