최근에 Shell Script를 이용해 bash를 만드는것을 우연히 접하게되었습니다. 처음에는 이게 뭐지 싶다가도 Linux Command를 활용하며 막상 내가 command shell을 shell script를 이용해서 만들어 보니까 다양한 활용방안도 생각할 수 있었고, 다른것보다 이 command shell을 만드는 것이 너무 재밌었습니다.
지금은 나중에 Back End작업에서 혹은 그냥 일상에서 리눅스 작업 환경에서 쓸 저만의 command shell을 제작하고 있습니다.
최근에 만든것은 covidkr이라는 명령어인데 이 명령어는 http://ncov.mohw.go.kr/ 에서제공하는 정보를 크롤링 하여, 정보를 텍스트 파일에 저장후 shell script가 읽어 정보를 display하는 방식입니다.
<크롤러 코드>
Github : https://github.com/J-hoplin1/MakeMyOwnShellScripts/blob/main/commandExecuteEngine/covid19.py
'''
공공데이터에서 코로나 API를 제공하고 있지만 API테스트 불가 등 API사용에 제한이 되어서 크롤러를 선택하였습니다.
This code written by Hoplin
This code is engine for command : covid19
This code return korea covid information
Scrape information from : http://ncov.mohw.go.kr/index.jsp
Data provided from Ministry of Health and Welfare of Korea
'''
from urllib.request import Request,urlopen
from urllib.request import URLError
from urllib.request import HTTPError
from urllib.parse import quote
from bs4 import BeautifulSoup
import json
import warnings
import os
#Return total new patient of today
def todayData(html) -> int:
getTodayOccurrence = list(html.findAll('span',{'class' : 'data'}))
getTodayOccurrence=[getTodayOccurrence[e].text for e in range(len(getTodayOccurrence))]
todayOccurence=sum(list(map(int,getTodayOccurrence)))
return todayOccurence
def totalData(html) -> list:
getDatas=list(html.findAll('span',{'class' : 'num'}))
getTotalDatas=[getDatas[e].text for e in range(len(getDatas))]
return getTotalDatas
def writeInFile(dataArray) -> None:
fp = open(os.path.dirname(os.path.realpath(__file__))+ "/covidInf.txt","w")
for t in dataArray:
cont = t + "\n"
fp.write(cont)
fp.close
try:
defaultData = "http://ncov.mohw.go.kr/index.jsp"
#Scrape HTML from datasource page
html = urlopen(defaultData)
bs = BeautifulSoup(html,'html.parser')
statusCode = bs.status
#scrape last data updated period
dataRecentUpdatedPeriod=[bs.find('span',{'class' : 'livedate'}).text.replace("(","").replace(")","")]
#Get layout HTML data : New patient occurrence today
newTodayData=bs.find('div',{'class' : 'liveNum_today_new'})
#Get layout HTML data : Total patient information
realtimeLiveData=bs.find('div',{'class' : 'liveNum'})
todayOccure = [str(todayData(newTodayData))]
totalDatas = totalData(realtimeLiveData)
# Data Array : ['Recent updated period','today new Patient', 'Total patient', 'Treatment Completed','Under Treatment','Dead']
retData = dataRecentUpdatedPeriod + todayOccure + totalDatas
writeInFile(retData)
except URLError as e:
erMSG="Wrong or Deleted URL. Please check again"
data = ["Error",erMSG]
writeInFile(data)
except HTTPError as e:
erMSG="HTTP Error, Status Code : " + statusCode
data = ["Error",erMSG]
writeInFile(data)
except BaseException as e:
erMSG="Engine Error : Something crashed or Incorrect Logic"
data = ["Error", erMSG]
writeInFile(data)
크롤러를 만들때는 그냥 평소처럼 만들었기에 별 다른 문제는 없었습니다. 하지만 문제가 있었습니다. 물론 아직 shell script에 대한 숙련도가 낮은 저의 문제일 가능성이 매우 높습니다. 제가 공부하면서 느낀거는 shell script에서는 python의 list와 같은 자료구조 사용에 있어서는 제한이 있을꺼라고 느꼈기에 일부러 크롤러가 데이터를 아래 사진과 같이 한줄씩 저장하게끔 하여서 읽게 만든후에 배열에 저장하려고 하였습니다.
하지만 IFS='\n' read으로 한 후에 for문을 이용하여 배열에 넣어보았지만, 저 6줄의 정보는 한줄이 되어 들어가있었습니다.
제가 찾은 해결 방안은 다음과 같습니다.
while문을 해석해 보면, 기본적으로 readData라는 변수에 저장된 text를 읽어옵니다. readData변수는 첫번째 줄에 있듯이, 파이썬 크롤러에서 저장한 text파일 경로입니다.
이렇게 되면 readData에는 다음과 같이 데이터가 저장됩니다.
"(text)\n(text)\n(text)"
결론적으로는 줄바꿈의 의미가 있는 이스케이프 시퀸스인 \n이 있더라도 줄바꿈 된 상태로 저장이 아닌 한줄로 저장이 되는것입니다.
read명령어의 -r옵션은 Raw모드이며, 백슬래시 기호를 이스케이프 시퀸스로 해석하지 않는다는 것을 의미합니다. 결론적으로 파일에서 글을 읽어들일때 줄이 끝나면서 줄바꿈이 되는 이스케이프 시퀸스인 \n을 이스케이프 시퀸스로 해석하지 않고 한줄 한줄씩 읽어 들여 line이라는 지정변수에 저장을 한다는 의미입니다.
참고 : https://unix.stackexchange.com/questions/209123/understanding-ifs-read-r-line
* 아직 shell script에 대해 초보라 잘못된 개념으로 이해했을수도 있습니다. 만약 포스트 내에서 틀린 개념이나 다른 의견이 있으시면 언제든지 피드백 부탁드립니다!
shell script 코드 : https://github.com/J-hoplin1/MakeMyOwnShellScripts
'Bash Shell' 카테고리의 다른 글
Shell Script 사용자 입력을 배열로 바로 저장하기 (0) | 2020.11.25 |
---|---|
Shell Script 출력 가로로 하기 (0) | 2020.11.25 |