리눅스 서버를 자동화하여 관리하기 위한 쉘 스크립트(Shell Script) 심화 강좌를 정리합니다.
출처 : inflearn
1. 정규 표현식
정규 표현식(Regular Expression)과 GREP
- 정규표현식이란
- 정규표현식은 다른 문자열을 검색하거나 치환할 목적으로 고안된 특수한 문자열
- grep, sed, awk 등 가장 강력한 유닉스 명령행 도구 중 일부는 정규표현식을 사용함
- 정규표현식의 예
- golf ;; 기본검색
- [Gg]olf ;; 대괄호 [] 사용하기
- 정규표현식을 써볼 수 있는 사이
- http://regexr.com
- PCRE(server)로 설정 후 실습
- 기본 연습 : https://regexr.com/38bd56
- 웹사이트 주소 패턴매칭 연습 : https://regexr.com/39p0t
- HTML5 CSS : https://regexr.com/38lmo
- IP 주소 : https://regexr.com/38odc
/a/g # a가 들어간 모든 글자
/[abcde]/g # abcde 가 들어간 모든 글자
/[a-zA-Z0-9]/g # 알파벳 대소문자, 모든숫자
/[^a-zA-Z0-9]/g # 알파벳 대소문자, 숫자를 제외한 모든 글자
/./g # 모든 글자
/.+/g # +는 어떤 글자의 반복을 나타냄
/.+ /g # 어떤 문자와 공백문자
/\w/g # /[a-zA-Z0-9]/g 와 동일
/./g # 모든 글자가 하나하나 선택됨.(공백문자 포함)
/.+/g # 모든 글자의 반복??
/\s/g # 공백 선택
/\S/g # 공백문자를 빼고 모든 글자가 선택됨.
/\W/g # 알파벳 대소문자를 제외한 문자 선택
/\d/g # 숫자만 선택됨
/\D/g # 숫자를 제외한 모든문자가 선택됨.
/\b.+?\b/g # 단어 선택
/n+/g # n이 반복되는 문자 (nn 이 있을 경우 하나로 인식)
# URL 패턴매칭
/http:\/\/www.website.com/g # 특수문자는 escape를 앞에 붙여야함.
/https?:\/\/.+/g #? 는 전글자가 있거나 없거나
/(https?:\/\/)?(www\.)?website\.com.+/g # website 가 들어간 url 선택
/#[0-9a-fA-F]+/g #HEX 값 선택
#CSS 패턴 매칭, rgb 기호 선택
/[rR][gG][bB]a?\([0-9\.\s%]{1,6},[0-9\.\s%]{1,6},[0-9.\ %]{1,6}(,[0-9\. %]{1,6})?\)/ig
# IP 주소 패턴 매칭
/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ig
# 찾고자 하는 문자열의 맨처음 단어만 선택
/^welcome/g
# welcome 이라는 문자가 나오기 전까지 패턴 매칭(greedy mode) - 큰 범위로 매칭
/.+welcome/g
# 좁은 범위로 매칭 (lazy mode)
/.+?welcome/g
2. 정규 표현식 비교(if..fi)
linux:/home/shkim$ read -p "Are you ready ?, press any key (y/n)" -n 1
Are you ready ?, press any key (y/n)
# regex 비교문(=~) 우측창에 정규표현식 패턴을 적는다
# ^ 시작, $ 끝을 나타냄
linux:/home/shkim$ if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi
- 정규표현식 비교문을 작성할때 인용부호는 사용하지 말아야 한다.
linux:/home/shkim$ locale='(..)_(..)'
# 환경변수 LANG은 locale 정보를 담고 있다.
linux:/home/shkim$ if [[ $LANG =~ $locale ]]
> then
> echo "Your country code is ${BASH_REMATCH[2]}."
> echo "Your language code is ${BASH_REMATCH[1]}."
> fi
Your country code is US.
Your language code is en.
linux:/home/shkim$ echo $LANG
en_US.UTF-8
DRILL : 사용자가 입력한 값이 숫자인지 아닌지 판별하는 스크립트를 작성해 보세요.
#정답
read -p "Enter a number" number
pat='^[0-9]+$'
if ! [[ $number =~ $pat ]]; then
echo "Not a number" >&2
else
echo "a number"
fi
3. 명령어(grep)
- Unix 시절 부터 써오던 문자열 탐색 명령어. 특정 문자열을 찾고자 할 때 사용하는 명령어.
linux:/home/shkim/shell_cmd$ cat president.txt
1. George Washington (1789-1797) John Adams (1789-1797)
2. John Adams (1797-1801) Thomas Jefferson (1797-1801)
3. Thomas Jefferson (1801-1809) Aaron Burr (1801-1805)
George Clinton (1805-1809)
4. James Madison (1809-1817) George Clinton (1809-1812)
None (1812-1813)
Elbridge Gerry (1813-1814)
None (1814-1817)
5. James Monroe (1817-1825) Daniel D. Tompkins (1817-1825)
6. John Quincy Adams (1825-1829) John C. Calhoun (1825-1829)
7. Andrew Jackson (1829-1837) John C. Calhoun (1829-1832)
None (1832-1833)
Martin Van Buren (1833-1837)
8. Martin Van Buren (1837-1841) Richard M. Johnson (1837-1841)
9. William Henry Harrison (1841) John Tyler (1841)
10. John Tyler (1841-1845) None (1841-1845)
11. James K. Polk (1845-1849) George M. Dallas (1845-1849)
12. Zachary Taylor (1849-1850) Millard Fillmore (1849-1850)
13. Millard Fillmore (1850-1853) None (1850-1853)
14. Franklin Pierce (1853-1857) William King (1853)
None (1853-1857)
15. James Buchanan (1857-1861) John C. Breckinridge (1857-1861)
16. Abraham Lincoln (1861-1865) Hannibal Hamlin (1861-1865)
Andrew Johnson (1865)
17. Andrew Johnson (1865-1869) None (1865-1869)
18. Ulysses S. Grant (1869-1877) Schuyler Colfax (1869-1873)
Henry Wilson (1873-1875)
None (1875-1877)
19. Rutherford B. Hayes (1877-1881) William Wheeler (1877-1881)
20. James A. Garfield (1881) Chester Arthur (1881)
21. Chester Arthur (1881-1885) None (1881-1885)
22. Grover Cleveland (1885-1889) Thomas Hendricks (1885)
None (1885-1889)
23. Benjamin Harrison (1889-1893) Levi P. Morton (1889-1893)
24. Grover Cleveland (1893-1897) Adlai E. Stevenson (1893-1897)
25. William McKinley (1897-1901) Garret Hobart (1897-1899)
None (1899-1901)
Theodore Roosevelt (1901)
26. Theodore Roosevelt (1901-1909) None (1901-1905)
Charles Fairbanks (1905-1909)
27. William Howard Taft (1909-1913) James S. Sherman (1909-1912)
None (1912-1913)
28. Woodrow Wilson (1913-1921) Thomas R. Marshall (1913-1921)
29. Warren G. Harding (1921-1923) Calvin Coolidge (1921-1923)
30. Calvin Coolidge (1923-1929) None (1923-1925)
Charles Dawes (1925-1929)
31. Herbert Hoover (1929-1933) Charles Curtis (1929-1933)
32. Franklin D. Roosevelt (1933-1945) John Nance Garner (1933-1941)
Henry A. Wallace (1941-1945)
Harry S. Truman (1945)
33. Harry S. Truman (1945-1953) None (1945-1949)
Alben Barkley (1949-1953)
34. Dwight D. Eisenhower (1953-1961) Richard Nixon (1953-1961)
35. John F. Kennedy (1961-1963) Lyndon B. Johnson (1961-1963)
36. Lyndon B. Johnson (1963-1969) None (1963-1965)
Hubert Humphrey (1965-1969)
37. Richard Nixon (1969-1974) Spiro Agnew (1969-1973)
None (1973)
Gerald Ford (1973-1974)
38. Gerald Ford (1974-1977) None (1974)
Nelson Rockefeller (1974-1977)
39. Jimmy Carter (1977-1981) Walter Mondale (1977-1981)
40. Ronald Reagan (1981-1989) George Bush (1981-1989)
41. George Bush (1989-1993) Dan Quayle (1989-1993)
42. Bill Clinton (1993-2001) Al Gore (1993-2001)
43. George W. Bush (2001-2009) Dick Cheney (2001-2009)
44. Barack Obama (2009-2017) Joe Biden (2009-2017)
45. Donald Trump (2017- ) Mike Pence (2017- )
linux:/home/shkim/shell_cmd$ grep "George.*Washington" president.txt
1. George Washington (1789-1797) John Adams (1789-1797)
# George 라는 단어의 Count => -c
linux:/home/shkim/shell_cmd$ grep -c George president.txt
7
# 패턴 매칭이 일어난 부분부터 마지막까지 출력해주는 옵션 => -o
linux:/home/shkim/shell_cmd$ grep -o 'George.*' president.txt
George Washington (1789-1797) John Adams (1789-1797)
George Clinton (1805-1809)
George Clinton (1809-1812)
George M. Dallas (1845-1849)
George Bush (1981-1989)
George Bush (1989-1993) Dan Quayle (1989-1993)
George W. Bush (2001-2009) Dick Cheney (2001-2009)
# 파일내의 발견된 위치 정보(단위 바이트 옵셋)
linux:/home/shkim/shell_cmd$ grep -bo 'George.*' president.txt
3:George Washington (1789-1797) John Adams (1789-1797)
171:George Clinton (1805-1809)
230:George Clinton (1809-1812)
741:George M. Dallas (1845-1849)
2820:George Bush (1981-1989)
2848:George Bush (1989-1993) Dan Quayle (1989-1993)
2951:George W. Bush (2001-2009) Dick Cheney (2001-2009)
# Donald 또는 Obama
linux:/home/shkim/shell_cmd$ grep 'Donald\|Obama' president.txt
44. Barack Obama (2009-2017) Joe Biden (2009-2017)
45. Donald Trump (2017- ) Mike Pence (2017- )
linux:/home/shkim/shell_cmd$ egrep 'Donald|Obama' president.txt
44. Barack Obama (2009-2017) Joe Biden (2009-2017)
45. Donald Trump (2017- ) Mike Pence (2017- )
# POSIX 패턴 => [[:digit:]]
linux:/home/shkim/shell_cmd$ grep -E '^\d{1,2}\. George' president.txt
linux:/home/shkim/shell_cmd$ grep -E '^[[:digit:]]{1,2}\. George' president.txt
1. George Washington (1789-1797) John Adams (1789-1797)
41. George Bush (1989-1993) Dan Quayle (1989-1993)
43. George W. Bush (2001-2009) Dick Cheney (2001-2009)
# 대소문자 구분하지 않고 검색 => -i
linux:/home/shkim/shell_cmd$ grep -i 'richard' president.txt
8. Martin Van Buren (1837-1841) Richard M. Johnson (1837-1841)
34. Dwight D. Eisenhower (1953-1961) Richard Nixon (1953-1961)
37. Richard Nixon (1969-1974) Spiro Agnew (1969-1973)
# 지정한 패턴을 제외하는 옵션 => -v
linux:/home/shkim/shell_cmd$ grep -i 'richard' president.txt | grep -v 'Nixon'
8. Martin Van Buren (1837-1841) Richard M. Johnson (1837-1841)
# 빈줄을 찾아주는 옵션 => ^$
linux:/home/shkim/shell_cmd$ grep -c "^$" president.txt
0 # 빈줄없음
# 1980년대 해당하는 것은 모두 찾아라. $ 맨 마지막 패턴 매칭
linux:/home/shkim/shell_cmd$ grep -E '198[[:digit:]]\)$' president.txt
39. Jimmy Carter (1977-1981) Walter Mondale (1977-1981)
40. Ronald Reagan (1981-1989) George Bush (1981-1989)
# -A1 : after , -B1 : before // 198이 들어가는 line의 앞의 한줄, 뒤의 한줄 출력
linux:/home/shkim/shell_cmd$ grep -A1 -B1 -E '198[[:digit:]]\)$' president.txt
Nelson Rockefeller (1974-1977)
39. Jimmy Carter (1977-1981) Walter Mondale (1977-1981)
40. Ronald Reagan (1981-1989) George Bush (1981-1989)
41. George Bush (1989-1993) Dan Quayle (1989-1993)
# 첫글자가 숫자가 아니면서, 라인 처음에 발견되는 문자열 출력
linux:/home/shkim/shell_cmd$ egrep '^[^0-9]' president.txt
George Clinton (1805-1809)
None (1812-1813)
Elbridge Gerry (1813-1814)
None (1814-1817)
None (1832-1833)
Martin Van Buren (1833-1837)
None (1853-1857)
Andrew Johnson (1865)
Henry Wilson (1873-1875)
None (1875-1877)
None (1885-1889)
None (1899-1901)
Theodore Roosevelt (1901)
Charles Fairbanks (1905-1909)
None (1912-1913)
Charles Dawes (1925-1929)
Henry A. Wallace (1941-1945)
Harry S. Truman (1945)
Alben Barkley (1949-1953)
Hubert Humphrey (1965-1969)
None (1973)
Gerald Ford (1973-1974)
Nelson Rockefeller (1974-1977)
linux:/home/shkim/shell_cmd$ ifconfig eth0 | grep 'inet' | cut -d: -f2 | awk '{print $2}'
172.16.1.166
# 일반 검색용 찾기(find)명령
linux:/home/shkim/shell_cmd$ fgrep '(1974' president.txt
38. Gerald Ford (1974-1977) None (1974)
Nelson Rockefeller
linux:/home/shkim/a$ seq 1 100 > nums
linux:/home/shkim/a$ cat nums
1
2
3
4
5
.
.
99
100
linux:/home/shkim/a$ sed -n '15,18p' nums
15
16
17
18
# George 라는 단어를 Tom 으로 변경
linux:/home/shkim/a$ sed 's/George/Tom/' president.txt | grep -n Tom
1:1. Tom Washington (1789-1797) John Adams (1789-1797)
4:Tom Clinton (1805-1809)
5:4. James Madison (1809-1817) Tom Clinton (1809-1812)
9:5. James Monroe (1817-1825) Daniel D. Tompkins (1817-1825)
17:11. James K. Polk (1845-1849) Tom M. Dallas (1845-1849)
63:40. Ronald Reagan (1981-1989) Tom Bush (1981-1989)
64:41. Tom Bush (1989-1993) Dan Quayle (1989-1993)
66:43. Tom W. Bush (2001-2009) Dick Cheney (2001-2009)
linux:/home/shkim/a$ sed 's/1981/2981/' president.txt | grep -n 2981
62:39. Jimmy Carter (1977-2981) Walter Mondale (1977-1981)
63:40. Ronald Reagan (2981-1989) George Bush (1981-1989)
# g 모두 변경
linux:/home/shkim/a$ sed 's/1981/2981/g' president.txt | grep -n 2981
62:39. Jimmy Carter (1977-2981) Walter Mondale (1977-2981)
63:40. Ronald Reagan (2981-1989) George Bush (2981-1989)
# -E 확장된 정규표현식을 지원해준는 옵션
linux:/home/shkim/a$ sed -E '/\(/s/\(.*//g' president.txt | more
1. George Washington
2. John Adams
3. Thomas Jefferson
George Clinton
4. James Madison
None
...
linux:/home/shkim/a$ sed 's/.$//' president.txt
1. George Washington (1789-1797) John Adams (1789-1797
2. John Adams (1797-1801) Thomas Jefferson (1797-1801
3. Thomas Jefferson (1801-1809) Aaron Burr (1801-1805
George Clinton (1805-1809
4. James Madison (1809-1817) George Clinton (1809-1812
...
linux:/home/shkim/a$ SP=$' ';TAB=$'\t';sed -E 's/'"(${SP}|${TAB})"'{2,5}.+$//' president.txt
1. George Washington (1789-1797)
2. John Adams (1797-1801)
3. Thomas Jefferson (1801-1809) Aaron Burr (1801-1805)
George Clinton (1805-1809)
4. James Madison (1809-1817)
None (1812-1813)
Elbridge Gerry (1813-1814)
None (1814-1817)
...
linux:/home/shkim/a$ echo -en msdos says "\"hello world\"\x0a\x0d" > msdos.txt
linux:/home/shkim/a$ cat msdos.txt
msdos says "hello world"
# sed 명령을 통해 msdos 형식의 파일을 linux 형식으로 변경할 수 있다.
linux:/home/shkim/a$ hexdump -C msdos.txt
00000000 6d 73 64 6f 73 20 73 61 79 73 20 22 68 65 6c 6c |msdos says "hell|
00000010 6f 20 77 6f 72 6c 64 22 0a 0d |o world"..|
0000001a
# 마지막 글자를 지우는 명령어.
linux:/home/shkim/a$ sed 's/.$//' msdos.txt
msdos says "hello world
linux:/home/shkim/a$ hexdump -C msdos.txt > msdos_modified.txt
linux:/home/shkim/a$ hexdump -C msdos.txt
00000000 6d 73 64 6f 73 20 73 61 79 73 20 22 68 65 6c 6c |msdos says "hell|
00000010 6f 20 77 6f 72 6c 64 22 0a 0d |o world"..|
0000001a
# 첫번째 필드만 출력 콜론이 나올때 까지.
linux:/home/shkim/a$ sed -E 's/([^:]*).*/\1/g' /etc/passwd
root
bin
daemon
adm
lp
sync
shutdown
halt
...
# 히어스트링 : <<< 첫글자 대문자로 변환
linux:/home/shkim/a$ sed -E "s/\b(.)/\u\1/g" <<< "hello world"
Hello World
linux:/home/shkim/a$ sed -E "s/\b(.)/\l\1/g" <<< "HELLO WORLD"
hELLO wORLD
4. 중괄호 확장
linux:/home/shkim/a$ touch noname{1,2,3,4,5,6,7,8,9}
linux:/home/shkim/a$ ls
noname1 noname2 noname3 noname4 noname5 noname6 noname7 noname8 noname9
linux:/home/shkim/a$ mkdir dir{1..9}
linux:/home/shkim/a$ ls
dir1 dir2 dir3 dir4 dir5 dir6 dir7 dir8 dir9
linux:/home/shkim/a$ mkdir -p newproject/{lib,doc/{pdf,ppt,doc},src,include/sys,bin}
linux:/home/shkim/a$ tree
.
└── newproject
├── bin
├── doc
│ ├── doc
│ ├── pdf
│ └── ppt
├── include
│ └── sys
├── lib
└── src
10 directories, 0 files
# eval을 사용하면 입력라인이 두번 구문 분석됩니다.
# 코드를 동적으로 평가하는데 사용됩니다.
linux:/home/shkim/a$ length=40
linux:/home/shkim/a$ eval printf -v line '%.0s-' {1..$length}
linux:/home/shkim/a$ echo $line
----------------------------------------
linux:/home/shkim/a$ printf -v line '%.0s-' {1..$length}
linux:/home/shkim/a$ echo $line
-
linux:/home/shkim/a$ cp president.txt{,.bak}
linux:/home/shkim/a$ ls
president.txt president.txt.bak
5. 명령 대체
linux:/home/shkim$ LANG=C; date
Tue Apr 6 22:10:38 KST 2021
# 가급적이면 가독성을 높이기 위해서 $() 형태를 사용하는 것이 좋다.
linux:/home/shkim$ LANG=C; echo "today is $(date)"
today is Tue Apr 6 22:11:47 KST 2021
linux:/home/shkim$ LANG=C; echo "today is `date`"
today is Tue Apr 6 22:13:10 KST 2021
# cd dir1 이 실패하면 우측 mkdir dir1으로 디렉토리를 만들고 dir1 으로 이동 후 현재경로 확인
linux:/home/shkim$ export DIR1="$( cd dir1 || { mkdir dir1 && cd dir1;}; pwd )"
linux:/home/shkim$ echo $DIR1
/home/shkim/dir1
6. 산술확장
linux:/home/shkim$ v1=12
linux:/home/shkim$ v2=$(($v1+34))
linux:/home/shkim$ echo $v2
46
linux:/home/shkim$ v3=$(($v1+$v2)); echo $v3
58
# 산술확장 내부에서는 변수명에 달러기호 $를 생략할 수 있다.
linux:/home/shkim$ v3=$((v1+v2)); echo $v3
58
linux:/home/shkim$ ((v3++))
linux:/home/shkim$ echo $v3
59
linux:/home/shkim$ ((v3+1)); echo $v3 # c언어 복합대입연산자
linux:/home/shkim$ if true; then echo true; fi
true
linux:/home/shkim$ if ((true));then echo true; fi
linux:/home/shkim$ if ((0)); then echo true; fi # ShellScript에서 0은 거짓 1은 참
linux:/home/shkim$ if ((1)); then echo true; fi
true
linux:/home/shkim$ if $((true)); then echo true; fi
0: command not found
linux:/home/shkim$
7. exit와 종료상태
linux:/home/shkim$ rm myfile; echo $?
rm: cannot remove 'myfile': No such file or directory
1
linux:/home/shkim$ rm myfile || { echo 'Could not delete file!' } >&2; exit 1;}
rm: cannot remove 'myfile': No such file or directory
Could not delete file!
logout
Connection to 192.168.16.213 closed.
- exit 0 과 exit 1의 차이점
- 쉘 종료시 오류의 발생여부 차이로 해석하게 됨. exit 1 => 실패 종료
8. 논리 연산 || 와 &&
# commmand1 || command2
# command1 이 성공적으로 실행되지 않은 경우에만 command2를 실행합니다.
linux:/home/shkim$ ls dir || { mkdir dir; }
# commmand1 && command2
# command1 이 성공적으로 실행된 경우에만 command2를 실행합니다.
linux:/home/shkim/dir$ cd mdir && ./myscript
9. 로그인쉘과 비 로그인 쉘
linux:/root$ shopt -q login_shell && echo 'login' || echo 'not-login'
login
linux:/root$
10. 명령분리자
linux:/home/shkim$ if true; then echo true; fi
true
linux:/home/shkim$ if true
> then echo true
> fi
true
linux:/home/shkim$ tool=saw; echo $tool
saw
linux:/home/shkim$ tool=hammer echo $tool # 명령 분리자 ; 가 생략되어 적용되지 않음.
saw
linux:/home/shkim$ echo $tool
saw
11. 인라인 그룹
linux:/home/shkim/test$ ls dir3 && echo "dir directory is here" || mkdir dir3 && echo "dir directory is made"
ls: cannot access dir3: No such file or directory
dir directory is made
linux:/home/shkim/test$ ls
dir3
linux:/home/shkim/test$ ls dir3 && echo "dir directory is here" || { mkdir dir3 && echo "dir directory is made";}
dir directory is here
linux:/home/shkim/test$ rm -rf dir3/
# 인라인 그룹 사용 방법 { ;} // 마지막에 세미콜론 유의
linux:/home/shkim/test$ ls dir3 && echo "dir directory is here" || { mkdir dir3 && echo "dir directory is made";}
ls: cannot access dir3: No such file or directory
dir directory is made
linux:/home/shkim/test$ ls
dir3
PREVIOUS섹션 3. 쉘 스크립트(ShellScript)
NEXT섹션 5. 비교와 루프문