반응형
리눅스 계열 OS에서 많이 사용되는 쉘스크립트의 형식(인덴트:indent 와 같은 줄맞춤?)을 자동으로 해주는 파이썬 라이브러리가 있어 스크랩한다.
쉘스크립트 경우 대부분 작성을 vim이나 notepad같은 별도의 IDE를 사용하지 않기때문에 여타 IDE처럼 인덴트를 자동으로 잡아주는 기능이 없다.
파이썬 파일로 아래 내용을 저장후 해당파일을 실행해주면 된다.
공식사이트는 https://arachnoid.com/python/beautify_bash_program.html 이며 라이센스는 GNU General Public License.
실행명령:
python beautify_bash.py file1.sh
파일:
소스코드:
#!/usr/bin/env python # -*- coding: utf-8 -*- #************************************************************************** # Copyright (C) 2011, Paul Lutus * # * # This program is free software; you can redistribute it and/or modify * # it under the terms of the GNU General Public License as published by * # the Free Software Foundation; either version 2 of the License, or * # (at your option) any later version. * # * # This program is distributed in the hope that it will be useful, * # but WITHOUT ANY WARRANTY; without even the implied warranty of * # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * # GNU General Public License for more details. * # * # You should have received a copy of the GNU General Public License * # along with this program; if not, write to the * # Free Software Foundation, Inc., * # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * #************************************************************************** import re, sys PVERSION = '1.0' class BeautifyBash: def __init__(self): self.tab_str = ' ' self.tab_size = 2 def read_file(self,fp): with open(fp) as f: return f.read() def write_file(self,fp,data): with open(fp,'w') as f: f.write(data) def beautify_string(self,data,path = ''): tab = 0 case_stack = [] in_here_doc = False defer_ext_quote = False in_ext_quote = False ext_quote_string = '' here_string = '' output = [] line = 1 for record in re.split('\n',data): record = record.rstrip() stripped_record = record.strip() # collapse multiple quotes between ' ... ' test_record = re.sub(r'\'.*?\'','',stripped_record) # collapse multiple quotes between " ... " test_record = re.sub(r'".*?"','',test_record) # collapse multiple quotes between ` ... ` test_record = re.sub(r'`.*?`','',test_record) # collapse multiple quotes between \` ... ' (weird case) test_record = re.sub(r'\\`.*?\'','',test_record) # strip out any escaped single characters test_record = re.sub(r'\\.','',test_record) # remove '#' comments test_record = re.sub(r'(\A|\s)(#.*)','',test_record,1) if(not in_here_doc): if(re.search('<<-?',test_record)): here_string = re.sub('.*<<-?\s*[\'|"]?([_|\w]+)[\'|"]?.*','\\1',stripped_record,1) in_here_doc = (len(here_string) > 0) if(in_here_doc): # pass on with no changes output.append(record) # now test for here-doc termination string if(re.search(here_string,test_record) and not re.search('<<',test_record)): in_here_doc = False else: # not in here doc if(in_ext_quote): if(re.search(ext_quote_string,test_record)): # provide line after quotes test_record = re.sub('.*%s(.*)' % ext_quote_string,'\\1',test_record,1) in_ext_quote = False else: # not in ext quote if(re.search(r'(\A|\s)(\'|")',test_record)): # apply only after this line has been processed defer_ext_quote = True ext_quote_string = re.sub('.*([\'"]).*','\\1',test_record,1) # provide line before quote test_record = re.sub('(.*)%s.*' % ext_quote_string,'\\1',test_record,1) if(in_ext_quote): # pass on unchanged output.append(record) else: # not in ext quote inc = len(re.findall('(\s|\A|;)(case|then|do)(;|\Z|\s)',test_record)) inc += len(re.findall('(\{|\(|\[)',test_record)) outc = len(re.findall('(\s|\A|;)(esac|fi|done|elif)(;|\)|\||\Z|\s)',test_record)) outc += len(re.findall('(\}|\)|\])',test_record)) if(re.search(r'\besac\b',test_record)): if(len(case_stack) == 0): sys.stderr.write( 'File %s: error: "esac" before "case" in line %d.\n' % (path,line) ) else: outc += case_stack.pop() # sepcial handling for bad syntax within case ... esac if(len(case_stack) > 0): if(re.search('\A[^(]*\)',test_record)): # avoid overcount outc -= 2 case_stack[-1] += 1 if(re.search(';;',test_record)): outc += 1 case_stack[-1] -= 1 # an ad-hoc solution for the "else" keyword else_case = (0,-1)[re.search('^(else)',test_record) != None] net = inc - outc tab += min(net,0) extab = tab + else_case extab = max(0,extab) output.append((self.tab_str * self.tab_size * extab) + stripped_record) tab += max(net,0) if(defer_ext_quote): in_ext_quote = True defer_ext_quote = False if(re.search(r'\bcase\b',test_record)): case_stack.append(0) line += 1 error = (tab != 0) if(error): sys.stderr.write('File %s: error: indent/outdent mismatch: %d.\n' % (path,tab)) return '\n'.join(output), error def beautify_file(self,path): error = False if(path == '-'): data = sys.stdin.read() result,error = self.beautify_string(data,'(stdin)') sys.stdout.write(result) else: # named file data = self.read_file(path) result,error = self.beautify_string(data,path) if(data != result): # make a backup copy self.write_file(path + '~',data) self.write_file(path,result) return error def main(self): error = False sys.argv.pop(0) if(len(sys.argv) < 1): sys.stderr.write('usage: shell script filenames or \"-\" for stdin.\n') else: for path in sys.argv: error |= self.beautify_file(path) sys.exit((0,1)[error]) # if not called as a module if(__name__ == '__main__'): BeautifyBash().main()
'Programming' 카테고리의 다른 글
[VS2015] Visual Studio 2015 Setup Project Unrecoverable build error 발생시 (0) | 2019.06.24 |
---|---|
[C++] XML Parser <CMarkup> (0) | 2019.06.19 |
[C++, STL] 범위기반 반복문으로 Iterator 쉽게 사용하기 (0) | 2019.04.16 |
[C#] 로또 번호 생성 코드 (Random, List) (0) | 2019.04.12 |
[MFC] CRect 이용 사각형 겹치는 영역 구하기 (0) | 2019.04.10 |