Branch
Branch 명령과 레이블을 이용하면 sed 에서도 if
~ else
문을 구현할 수 있습니다.
# 입력 라인이 '000' 일 경우 'AAA' 로 출력하고 그렇지 않을경우 'BBB' 로 출력
$ echo -e '111\n000\n222' | sed -n 's/000/AAA/; tX; s/.*/BBB/; :X p'
BBB # t 명령과 X 레이블 사용
AAA
BBB
$ echo -e '111\n000\n222' | sed -n '/000/{ s//AAA/; bX }; s/.*/BBB/; :X p'
BBB # b 명령과 X 레이블 사용
AAA
BBB
b
label , t
label , T
label
Unconditional branch
1. 이 명령은 무조건 해당 label 로 분기합니다.
2. label 을 적지 않을 경우 명령 사이클의 END 로 분기하고 다음 사이클을 시작합니다.
Conditional branch (test)
1. t
명령은 test flag 가 1 일 경우 분기, T
명령은 test flag 가 0 일 경우 분기합니다.
2. flag 값이 1 이 되는 때는 s/../../
명령이 성공했을 때입니다.
3. flag 값이 0 이 되는 때는 t
명령으로 분기할 때, 라인을 읽어들일 때입니다.
4. default flag 값은 0 입니다.
그러므로
s/../../
명령이 성공했을 경우t
명령에서 분기하게 되고
실패했을 경우는T
명령에서 분기하게 됩니다.
5. test 에 성공했으나 label 을 적지 않았을 경우 END 로 분기하고 다음 사이클을 시작합니다.
조건 분기 명령을 다룰 때 잘못 생각하기 쉬운 부분이 s/../../
명령이 실패하면 flag 값이 0
이 될거라는 생각입니다.
예를 들어 다음과 같은 시리즈의 명령이 있을때 T
명령은 테스트에 실패하여 분기하지 않고 t
명령은 분기합니다.
# 앞에 (성공) 이 있으므로 현재 flag 값은 1 이다.
s/../../(성공); s/../../(실패); T XX1; t XX2
이제 t
명령으로 레이블 XX2 로 분기했으므로 이제는 flag 값이 1 에서 0 으로 되었습니다.
그러므로 다음과 같은 명령이 이어지면 T
명령에서는 YY1 로 분기하지만 t
명령은 분기하지 않습니다.
# 't' 명령으로 분기했으므로 이제 flag 값은 0 이다.
T YY1; t YY2
이와 같은 동작 방식은 s
명령을 여러개 사용하게 될때 문제가 될 수 있는데요.
가령 앞선 s
명령이 성공이 돼버리면 뒤에 이어지는 s
명령에서는 성공, 실패를 구분해서
분기할 수가 없게 됩니다.
이럴때는 다음과 같이 s
명령 사용전에 flag 값을 0 으로 reset 해놓는 것이 필요합니다.
# 1. ( ) 괄호로 둘러싸인 입력 스트링에서 먼저 ( ) 를 삭제하고
# 2. 그다음 "@" 문자를 ":" 로 치환하는데 성공하면 결과를 출력하고
# 3. 실패할 경우는 출력을 하지 않습니다.
# s/@/:/g 가 성공하면 tX 에의해 :X 레이블로 분기해 p 출력이 되게하고
# 실패할 경우는 b 명령에 의해 END 로 분기하여 출력이 되지 않게한다.
$ echo '(foo@bar@zoo)' | sed -En -e 's/\(|\)//g; s/@/:/g; tX; b; :X p'
foo:bar:zoo
# 또는
$ echo '(foo@bar@zoo)' | sed -En -e 's/\(|\)//g; s/@/:/g; T; p'
foo:bar:zoo
# 하지만 s/@/:/g 명령이 실패하였는데도 앞선 s 명령이 성공하여 값이 출력이 된다.
$ echo '(foo/bar/zoo)' | sed -En -e 's/\(|\)//g; s/@/:/g; tX; b; :X p'
foo/bar/zoo
# s/@/:/g 명령 사용전에 tR :R 를 이용해 flag 값을 0 으로 리셋한다.
# s/@/:/g 명령이 실패하여 기대하였던 것과 같이 값이 출력되지 않는다.
$ echo '(foo/bar/zoo)' | sed -En -e 's/\(|\)//g; tR :R s/@/:/g; tX; b; :X p'
$
substitute 명령의 g 플래그 와 branch 명령 비교
substitute 명령의 g
플래그
$ echo "1234567890" | sed -E 's#([0-9]{2})([0-9]{2})#\1\\x\2#g'
12\x3456\x7890
$ echo "1234567890" | sed -E 's#([0-9]{2})([0-9]{2})#\\x\1\\x\2#g'
\x12\x34\x56\x7890
$ echo "1234567890" | sed -E 's#([0-9]{2})#\\x\1#g'
\x12\x34\x56\x78\x90
# *, + 수량 한정자를 사용하면 마지막 매칭값만 출력된다.
$ echo "1234567890" | sed -E 's#([0-9]{2})*#\\x\1#g'
\x90
branch 명령을 사용
$ echo '1234567890' | sed -E ':X s/^([0-9]*)([0-9]{2})/\1\\x\2/; tX'
\x12\x34\x56\x78\x90
$ echo '1234567890' | sed -E ':X s/^([0-9]*)([0-9]{2})/\1\\x\2/; l; tX' # 'l' 명령 사용
12345678\\x90$
123456\\x78\\x90$
1234\\x56\\x78\\x90$
12\\x34\\x56\\x78\\x90$
\\x12\\x34\\x56\\x78\\x90$
\\x12\\x34\\x56\\x78\\x90$
\x12\x34\x56\x78\x90
예제 1
다음은 [ ]
괄호안에 포함되지 않는 단어들을 추출하는 예입니다.
단순히 괄호 안의 내용을 s
명령을 이용해 모두 삭제하면 될것 같지만
문제는 괄호가 중첩되어 있는 경우가 있다는 것입니다.
따라서 이와 같은 경우는 두번째 명령과 같이 branch loop 기능을 활용해야 합니다.
$ cat file1
[--natsettings<1-N> [<mtu>],[<socksnd>], [<sockrcv>],[<tcpsnd>], [<tcprcv>]] hello
[--natpf<1-N> [<rulename>],tcp|udp,[<hostip>], <hostport>,[<guestip>],<guestport>]
[--natpf<1-N> delete <rulename>] world [--nataliasmode default|proxyonly, [sameports]]
$ sed -E 's/\[[^][]*\]//g' file1 # 괄호가 중첩되어 있어서 올바로 삭제되지 않는다.
[--natsettings<1-N> ,, ,, ] hello
[--natpf<1-N> ,tcp|udp,, <hostport>,,<guestport>]
world [--nataliasmode default|proxyonly, ]
$ sed -E ':X s/\[[^][]*\]//g; tX' file1 # branch loop 를 사용해야 한다.
hello
world
예제 2
이번 예제는 파일에서 --string
형식의 스트링을 추출하는 것입니다.
먼저 파일 내용을 하나의 라인으로 만들기 위해 echo $(< file2)
를 사용했는데 이때 globbing 이 발생할 수 있으므로 set -f
옵션을 설정했습니다.
위 예제에 비해 좀 복잡해 보일 수 있는데 backreference 의 순서를 \2\n\1
와 같이
활용하는 것을 주의깊게 보세요.
$ cat file2
Usage: VBoxManage sharedfolder add <uuid|vmname> --name <name>
--hostpath <hostpath> [--readonly] [--automount] VBoxManage
sharedfolder remove <uuid|vmname> --name <name> [--transient]
# 매칭이 되는 \2 는 계속 앞쪽으로 붙여넣기가 되고 \3 은 삭제가 된다. \1 의 (.*) 는 greedy 매칭
$ { set -f; echo $(< file2) ;} |
sed -En ':X s/(.*)(--[a-z-]+)(.*)/\2\n\1/; /--[a-z-]+\n$/{p;q}; tX'
--transient
--name
--hostpath
--readonly
--automount
--name
# `l` 명령을 이용하여 디버깅을 해보면 다음과 같습니다.
$ { set -f; echo `< file2` ;} |
sed -En ':X s/(.*)(--[a-z-]+)(.*)/\2\n\1/; l; /--[a-z-]+\n$/{p;q}; tX'
--transient\nUsage: VBoxManage sharedfolder add <uuid|vmname> --name \
<name> --hostpath <hostpath> [--readonly] [--automount] VBoxManage sh\
aredfolder remove <uuid|vmname> --name <name> [$
--name\n--transient\nUsage: VBoxManage sharedfolder add <uuid|vmname>\
--name <name> --hostpath <hostpath> [--readonly] [--automount] VBoxM\
anage sharedfolder remove <uuid|vmname> $
--automount\n--name\n--transient\nUsage: VBoxManage sharedfolder add \
<uuid|vmname> --name <name> --hostpath <hostpath> [--readonly] [$
--readonly\n--automount\n--name\n--transient\nUsage: VBoxManage share\
dfolder add <uuid|vmname> --name <name> --hostpath <hostpath> [$
--hostpath\n--readonly\n--automount\n--name\n--transient\nUsage: VBox\
Manage sharedfolder add <uuid|vmname> --name <name> $
--name\n--hostpath\n--readonly\n--automount\n--name\n--transient\nUsa\
ge: VBoxManage sharedfolder add <uuid|vmname> $
--transient\n--name\n--hostpath\n--readonly\n--automount\n--name\n$
--transient
--name
--hostpath
--readonly
--automount
--name
다음은 위와 비슷한 예제인데 {{string}}
형식의 스트링을 추출하는 것입니다.
$ cat file3
Usage: VBoxManage sharedfolder add <uuid|vmname> {{name}}
<name> {{hostpath}} <hostpath> {{readonly}} {{automount}}
VBoxManage sharedfolder remove <uuid|vmname> {{name}} <name> {{transient}}
$ { set -f; echo `< file3` ;} |
sed -En ':X s/(.*)(\{\{[a-z]+}})(.*)/\2\n\1/; /\{\{[a-z]+}}\n$/{p;q}; tX'
{{transient}}
{{name}}
{{hostpath}}
{{readonly}}
{{automount}}
{{name}}
예제 3
다음과 같은 xml 데이터가 이어질 경우 <ns1:queryDeviceInfoBtRequest....
에서부터
</ns1:queryDeviceInfoBtRequest>
까지 데이터를 출력하는데
그중에서 equipmentId
값이 089471386301077634
일 경우만 프린트하는 것입니다.
<ns1:queryDeviceInfoBtRequest xmlns:ns1="http://integration.sprint.com/test/">
<ns12:equipmentId>543211386301077634</ns12:equipmentId>
<ns3:messageHeaderVersion>2</ns3:messageHeaderVersion>
<ns3:serviceName>queryDeviceInfoBt</ns3:serviceName>
</ns1:queryDeviceInfoBtRequest>
<ns1:queryDeviceInfoBtRequest xmlns:ns1="http://integration.sprint.com/integration/">
<ns12:equipmentId>089471386301077634</ns12:equipmentId>
<ns3:messageHeaderVersion>2</ns3:messageHeaderVersion>
<ns3:serviceName>queryDeviceInfoBt</ns3:serviceName>
</ns1:queryDeviceInfoBtRequest>
---------------------------------------------------------
$ sed -En '
/<ns1:queryDeviceInfoBtRequest/!b # 첫라인과 매칭이 안되는 라인이면 끝으로 분기하고
h # 매칭이 되면 hold 합니다.
:X
n # pattern space 에 다음 라인을 읽어들여서
/<\/ns1:queryDeviceInfoBtRequest>/ { # 마지막 라인 매칭을 테스트 합니다.
H # 매칭이 되면 hold space 에 append 한후에(H)
g # 그동안 hold space 에 모아두었던 데이터를
/equipmentId>089471386301077634</p # pattern space 로 옮긴후(g) id 매칭을 테스트해서
b # 매칭이 되면 출력하고 그렇지 않으면 끝으로 분기합니다.
}
H # 마지막 라인과 매칭이 안되면 계속 hold space 에 append 하고
bX # 다음 라인을 읽어들이기 위해 :X 레이블로 분기합니다.
' xmlfile
예제 4
"header" { ... }
형태로 구성되는 레코드인데요.
이때 "crosshair"
헤더에 "sprites/crosshairs"
항목이 있을 경우 해당 레코드를 삭제합니다.
hold space 를 사용하지 않고 pattern space 만 이용하는 이 방법은
pattern space 에 모아두는 데이터가 커질수록 regex 매칭 영역이 계속해서 증가하므로 처리 속도가 크게 떨어질 수 있습니다.
"crosshair"
{
"file" "vgui/replay/thumbnails/"
"x" "0"
"y" "0"
"width" "64"
"height" "64"
}
"weapon"
{
"file" "sprites/bucket_bat_red"
"x" "0"
"y" "0"
"width" "200"
"height" "128"
}
"crosshair"
{
"file" "sprites/crosshairs" <---
"x" "32"
"y" "32"
"width" "32"
"height" "32"
}
"autoaim"
{
"file" "sprites/crosshairs"
"x" "0"
"y" "48"
"width" "24"
"height" "24"
}
-------------------------------------------
$ sed -f - <<\EOF file
/^"crosshair"$/ {
:X
N # "}" 라인이 입력될 때까지 pattern space 에 append 합니다.
/}$/!bX # "}" 와 매칭이 안될 경우 다음 라인을 읽어들이기 위해 :X 로 분기합니다.
# `}` 와 매칭이 되었다는 것은 현재 pattern space 에 "crosshair" {...} 레코드
# 전체가 입력되었다는 것을 의미하므로 "sprites/crosshairs" 항목을 조회하여
# 매칭될 경우 `d` 명령으로 pattern space 를 삭제하고 새로운 명령 사이클을 시작합니다.
/sprites\/crosshairs/d
}
EOF
예제 5
중복되는 단어 삭제 하기
$ echo "I am am a a boy am am a am boy am" |
sed -E ':X s#(\b\w+\b)(.*) \1#\1\2#g; tX'
I am a boy
$ echo "I am am a a boy am am a am boy am" |
sed -E ':X s#(\b\w+\b)(.*) \1#\1\2#g; l; tX' # 'l' 명령 사용
I am am a a boy am am a am boy$
I am am a a boy am am a boy$
I am am a a boy am a boy$
I am am a a boy a boy$
I am a a boy boy$
I am a boy$
I am a boy$
I am a boy
---------------------------------------------
아래에서 1번과 2번은 같은 스트링이 됩니다.
가령 1번이 'am' 과 매칭이 되었다면 2번도 'am' 이 되어 결과적으로
's#(am)(.*) am#\1\2#g' 와 같은 식이 됩니다.
식에 따라 '\1\2' 만 취하게 되면 마지막 'am' 은 삭제가 되겠죠.
마지막 'am' 이후의 스트링은 hidden 이므로 남게됩니다.
sed -E ':X s#(\b\w+\b)(.*) \1#\1\2#g; tX'
^^^^^^^^^ ^^
1번 2번
예제 6
천 단위 콤마 넣기
$ echo "12345678" | sed -E ':X s#\B[0-9]{3}\b#,&#; tX'
12,345,678
$ echo "12345678" | sed -E ':X s#\B[0-9]{3}\b#,&#; l; tX'
12345,678$
12,345,678$
12,345,678$
12,345,678
------------------------------------
식에서 맨앞에 '\B' 를 포함해야 합니다.
그렇지 않으면 '12,345,678' 상태에서 '[0-9]{3}' 이 계속 '345' 와 매칭이 되어
종료되지 않고 아래와 같이 됩니다.
'345' 는 앞, 뒤에 ',' 가 있어서 ',\b345\b,' 와 같은 상태인데 '\B' 를 붙이면
',\B345\b,' 가 되므로 매칭이 되지 않겠죠.
12,345,678
12,,345,678
12,,,345,678
12,,,,345,678
. . .
Quiz
입력값 중에서 foo
가 존재하면 bar
를 xxx
로 변경하고
존재하지 않으면 bar
를 zzz
로 변경하려면 어떻게 할까요?
s/foo/&/
형식을 이용하면 입력값은 변경되지 않고 test flag 만 설정할 수 있습니다.
$ echo "aaa bbb foo ccc bar ddd" |
sed -En -e 's/foo/&/; tX; s/bar/zzz/; p; b' -e ':X s/bar/xxx/; p'
aaa bbb foo ccc xxx ddd
$ echo "aaa bbb zoo ccc bar ddd" |
sed -En -e 's/foo/&/; tX; s/bar/zzz/; p; b' -e ':X s/bar/xxx/; p'
aaa bbb zoo ccc zzz ddd
---------------------------------------------------------
# 다음과 같이 해도 됩니다.
$ echo "aaa bbb foo ccc bar ddd" |
sed -En -e '/foo/{ s/bar/xxx/; p; b}; s/bar/zzz/; p'
aaa bbb foo ccc xxx ddd
$ echo "aaa bbb zoo ccc bar ddd" |
sed -En -e '/foo/{ s/bar/xxx/; p; b}; s/bar/zzz/; p'
aaa bbb zoo ccc zzz ddd
2 .
매칭되는 스트링을 다른 스트링으로 치환하는 것은 s
명령으로 쉽게 할수가 있는데요.
스트링의 length 를 그대로 유지하면서 다른 문자로 치환하려면 어떻게 할까요?
다음은 "VBoxManage natnetwork" 스트링의 length 를 유지하면서 space 로 치환합니다.
$ cat file
VBoxManage natnetwork add --netname <name>
VBoxManage natnetwork remove --netname <name>
VBoxManage natnetwork modify --netname <name>
$ cat file | sed -En -e 's/VBoxManage natnetwork/\a&\a/;' \
-e ':X s/\a.(.*)\a/ \a\1\a/; tX; s/\a\a//; p'
add --netname <name>
remove --netname <name>
modify --netname <name>
$ cat file | sed -En -e 's/VBoxManage natnetwork/\a&\a/;' \
-e ':X s/\a.(.*)\a/ \a\1\a/; tX; s/\a\a//; s/[ ]{6}/foobar/; p'
foobar add --netname <name>
foobar remove --netname <name>
foobar modify --netname <name>
# perl 의경우는 e (execute) 플래그를 이용하면 치환값에 함수를 사용할 수 있습니다.
$ cat file | perl -pe 's/(VBoxManage natnetwork)/" " x length($1)/e'
$ cat file | perl -pe 's/(VBoxManage natnetwork)/foobar . " " x (length($1) - 6)/e'
다음은 [Ss]h
로 시작하는 단어를 length 를 유지하면서 모두 .
으로 치환합니다.
$ echo "She sells sea shells by the sea shore" |
sed -E ':X s/([Ss]h\.*)[^. ]/\1./; tX; s/[Ss]h/../g'
... sells sea ...... by the sea .....
3 .
옵션 스트링에 [ ]
괄호를 이용해서 선택 값을 나타낼 수 있는데요.
다음과 같이 확장이 되게 스크립트를 작성하는 것입니다.
괄호가 없는 경우는 그대로 출력하면 됩니다.
--[no-]leading-underscore -fenable-[tree|rtl|ipa]-bar= -fbounds-check
--no-leading-underscore -fenable-ipa-bar= -fbounds-check
--leading-underscore -fenable-rtl-bar=
-fenable-tree-bar=
# 첫 번째의 경우는 비교적 간단합니다.
$ echo '--[no-]leading-underscore' | sed -En '
h; s/(.*)\[([^]]*)](.*)/\1\2\3/; p; # [ ] 괄호 안에 있는 스트링을 추출해서 출력
g; s/\[[^]]*]//; p; # [ ] 괄호 전체를 삭제해서 출력
'
--no-leading-underscore
--leading-underscore
# 두 번째가 좀 까다로운데 [tree|rtl|ipa] 값을 [(\|?(\w+))+] 을 이용해 추출하면
# \3 의 값은 마지막 ipa 가 됩니다. 그 다음 반복에서는 또 마지막 rtl 이 되고 ...
# 마지막 라인에서는 \|?\w+] 를 이용해 |ipa] 부분을 삭제해서 [tree|rtl] 로 만듭니다.
# 반복을 계속하다가 최종 [] 스트링만 남게되면 종료합니다.
$ echo '-fenable-[tree|rtl|ipa]-bar=' | sed -En '
:X h; tR1; :R1 s/(.*)\[(\|?(\w+))+](.*)/\1\3\4/; p; T;
g; s/\|?\w+]/]/; /\[]/!bX
'
-fenable-ipa-bar=
-fenable-rtl-bar=
-fenable-tree-bar=
--------------------------------------------------
# 위의 두 작업을 하나로 합치면 다음과같이 됩니다.
$ echo '-fenable-[tree|rtl|ipa]-bar=' | sed -En '
:X h; tR1; :R1 s/(.*)\[(\|?(\w+-?))+](.*)/\1\3\4/; p; T;
g; s/\|?\w+-?]/]/; tR2 :R2 s/-\[]([[:alnum:]])/-\1/p; t; /\[]/!bX'
4 .
텍스트 데이터 중에는 ( )
, < >
, { }
같은 괄호가 쌍으로 nesting 되어 있는 경우가
많습니다. 다음 스트링에서 괄호만 모두 제거하려면 어떻게 할까요?
첫번째 방법:
$ echo 'foo (aa, ((bb, (cc)), (dd))) bar (xx, ((yy), zz)) zoo' |
sed -E ':X s/\([^()]*\)//g; l; tX'
foo (aa, ((bb, ), )) bar (xx, (, zz)) zoo$
foo (aa, (, )) bar (xx, ) zoo$
foo (aa, ) bar zoo$
foo bar zoo$
foo bar zoo$
foo bar zoo
다음과 같이하면 괄호를 왼쪽에서부터 차례로 제거해 나갈수 있습니다.
$ echo 'foo (aa, ((bb, (cc)), (dd))) bar (xx, ((yy), zz)) zoo' |
sed -E ':X s/([^)]*)(\([^)]*\))/\1/; l; tX'
foo (aa, ((bb, ), (dd))) bar (xx, ((yy), zz)) zoo$
foo (aa, (, (dd))) bar (xx, ((yy), zz)) zoo$
foo (aa, (, )) bar (xx, ((yy), zz)) zoo$
foo (aa, ) bar (xx, ((yy), zz)) zoo$
foo bar (xx, ((yy), zz)) zoo$
foo bar (xx, (, zz)) zoo$
foo bar (xx, ) zoo$
foo bar zoo$
foo bar zoo$
foo bar zoo
-----------------------------------------------------------------
# 처음 (cc) 괄호가 삭제되는 과정을 살펴보면 다음과 같습니다.
1. 먼저 '[^)]*' regexp 에의해 'foo (aa, ((bb, (cc' 까지 매칭이 됩니다.
2. 그다음 '\([^)]*\)' regexp 에의해 '(...)' 형태가 매칭이 되어야 하는데
이때 위의 1번을 만족하면서 2번이 매칭이될수 있는 형태는 다음과 같이 됩니다.
foo (aa, ((bb, (cc)
^^^^^^^^^^^^^^^ ^^^^
1번 2번
3. 's/.../\1/' 식에 의해 1번만 취하게 되면 2번 이후는 hidden 이므로 결과적으로
2번이 삭제되게 됩니다.
다음은 __attr__( 괄호 )
형태의 스트링만 제거하는 것입니다.
$ echo '((foo)) __attr__((aa,(cc)),(xx)) ((bar)) __attr__((dd,((xx)),aa)) ((zoo))' |
sed -E ':X s/__attr__\s*\([^()]*\)//g; s/(__attr__[^)]*)(\([^)]*\))/\1/g; l; tX'
((foo)) __attr__((aa,),(xx)) ((bar)) __attr__((dd,(),aa)) ((zoo))$
((foo)) __attr__(,(xx)) ((bar)) __attr__((dd,,aa)) ((zoo))$
((foo)) __attr__(,) ((bar)) __attr__() ((zoo))$
((foo)) ((bar)) ((zoo))$
((foo)) ((bar)) ((zoo))$
((foo)) ((bar)) ((zoo))
5 .
다음은 괄호 안에 있는 ,
콤마를 모두 제거하는 것입니다.
$ echo "John Doe, model (dell, 24, inch), 847483992, (100, foo, infinity) loop" |
sed -E ':X s/(\([^)]*),/\1/g; l; tX'
John Doe, model (dell, 24 inch), 847483992, (100, foo infinity) loop$
John Doe, model (dell 24 inch), 847483992, (100 foo infinity) loop$
John Doe, model (dell 24 inch), 847483992, (100 foo infinity) loop$
John Doe, model (dell 24 inch), 847483992, (100 foo infinity) loop
---------------------------------------------------------------------
1. 먼저 '\([^)]*' 식에 의해 '(dell, 24, inch' 까지 매칭이 됩니다.
2. 그런데 위의식 바로뒤에 ',' 콤마가 붙었으므로 1번식 과 ',' 를 모두 만족하는 값은
'(dell, 24,' 까지가 됩니다.
3. 이후 '\1' 에의해 ',' 콤마를 제외한 부분만 취하게 되면 괄호안에 있는 콤마가 삭제됩니다.