[태그:] Expression
[Linker] GNU linker LD 사용하기 – Linker Scripts (.ld)
그냥 내용이나 조금 보려고 했는데, 보다보니 번역을 하고 있다.. 왜지…..
찾아보니 korea.gnu.org에 이미 번역된 문서(http://korea.gnu.org/manual/release/ld/ld-mahajjh/ld_3.html#SEC6)가 있다. 내건 부끄러우니 이걸 보도록 하자.
이 문서는 “GNU Free Documentation License”를 따른다.
Linker Scripts
원문: https://sourceware.org/binutils/docs/ld/Scripts.html#Scripts
번역: 양정석 (dasomoli@gmailREMOVETHIS.com)
모든 link는 linker script로 제어된다. 이 스크립트는 linker script language로 쓰여진다.
linker script의 주목적은 input file 내의 section을 output file로 어떻게 map해야 하는지, 그리고 output file의 memory layout을 어떻게 해야 하는지 설명하는 거다. 대부분의 linker script는 이 이상을 하지 않는다. 그러나 필요하면 linker script는 linker에게 아래에 설명된 command들을 사용해서 다른 동작을 수행할 수 있다.
linker는 언제나 linker script를 사용한다. 제공되지 않으면 linker는 linker executable 내부에 compile된 default script를 사용한다. '--verbose'
command line option을 사용해서 default linker script를 볼 수 있다. '-r'
이나 '-N'
같은 command line option은 default linker script에 영향을 준다.
자신만의 linker script를 '-T'
command line option을 사용해서 줄 수 있다. 이렇게 하면 그 linker script가 default linker script를 대신한다.
또한, linker에게 그것들이 link될 파일들이라도 input 파일로 그들을 이름 붙여서 암묵적으로 linker scripts를 사용할 수도 있다. Implicit Linker Scripts를 보라.
Linker Script 기본 개념
linker script language를 설명하기 위해서 기본적인 개념과 용어를 define할 필요가 있다.
링커는 input file들을 하나의 output file로 합친다. output file과 각 input file은 object file format이라고 알려진 special data format으로 되어 있다. 그래서 각 파일을 object file이라고 부른다. output file은 executable이라고 자주 부르지만, 우리의 목적에 따라 object file이라고도 부를거다. object file은 무엇보다도 sections들의 리스트를 가진다. input file 안에 있는 section을 input section이라고 하고, output file 안에 있는 section을 output section이라고 한다.
각 object file 안의 section은 하나의 name과 하나의 size를 갖는다. 거의 모든 섹션은 section contents라고 하는 data의 associated block도 갖는다. loadable로 표시된 section은 output file이 실행될 때 memory로 load된다. content가 없는 section은 allocatable일 수 있는데 이는 메모리는 set되지만 거기에 특별한 게 load되지는 않는다(특정 경우에 이 메모리는 0으로 초기화된다)는 뜻이다. loadable도 allocatable도 아닌 섹션은 일반적으로 디버깅 정보를 갖는다.
모든 loadable 또는 allocatable output section은 두가지 address를 갖는다. 먼저 VMA(virtual memory address)는 output file이 실행될 때 갖을 address이다. 다음으로 LMA(load memory address)는 그 section이 load될 address이다. 대부분의 경우에 두 address는 같다. 다를 수 있는 예는 data section이 ROM에 load되고 program이 시작될 때 RAM으로 복사될 때이다(이 테크닉은 ROM based system에서 global variables을 초기화할 때 자주 쓰인다). 이 경우 ROM address는 LMA가 되고, RAM address는 VMA가 될 것이다.
objdump
프로그램의 '-h'
옵션으로 object file의 section들을 볼 수 있다.
모든 object file은 symbol table이라고 부르는 symbols의 list를 갖는다. symbol은 defined 이거나 undefined일 수 있다. 각 symbol은 이름을 갖고, 각 defined symbol은 다른 정보들보다 address를 갖는다. C/C++을 object file로 컴파일하면 모든 defined function과 global/static variable의 defined symbol을 얻게 된다. input file에서 참조되는 모든 undefined function과 global variable은 undefined symbol이 될 거다.
nm
이나 objdump
프로그램의 '-t'
option로 object file의 symbol들을 볼 수 있다.
Linker Script 형식
Linker script는 text file이다.
command를 연결해서 linker script를 작성한다. 각 command는 argument가 붙을 수 있는 keyword이거나 또는 symbol로의 assignment 둘 중 하나다. ';'
으로 각 command를 구분할 수 있다. white space는 일반적으로 무시된다.
file이나 format name같은 string은 직접 들어갈 수 있다. file name에 다른 file name을 구별할 때 쓰이는 ','
같은 문자가 포함되어 있으면, ""
로 감쌀 수 있다. “”를 파일 이름으로 쓸 수 있는 방법은 없다.
C처럼 linker script안에 '/*'
, '*/'
로 comment를 달 수도 있다. C에서처럼 comment는 문법적으로 whitespace와 동등하다.
간단한 Linker Script 예제
많은 linker script들은 정말 간단하다.
가능한 가장 간단한 linker script는 딱 하나의 'SECTIONS'
command를 갖는 것이다. 'SECTIONS'
command는 output file의 memory layout을 설명하는데 쓰인다.
'SECTIONS'
command는 강력한 command이다. 여기서 우리는 가장 간단히 사용하는 것을 설명할거다. 여러분의 프로그램이 code, initialized data, uninitialized data로만 구성되어 있다고 하면 거기에는 각각 '.text'
, '.data'
, '.bss'
section이 있게 된다. 이에 더해서 여러분의 input files안에 있는 section들만 있다고 하자.
이 예제를 위해 그 code가 address 0x10000에 load되어야 하고, 그 data는 address 0x8000000에서 시작되어야 한다고 하자.
아래가 이를 위한 link script이다.
SECTIONS { . = 0x10000; .text : { *(.text) } . = 0x8000000; .data : { *(.data) } .bss : { *(.bss) } }
'SECTIONS'
command는 'SECTIONS'
키워드, 그 뒤에 symbol assignments과 output section 설명을 ‘{‘, ‘}’로 감싸서 쓴다.
위 예제의 첫 줄은 location counter인 special symbol ‘.’을 지정한다. 다른 방법(이건 뒤에 설명)으로 output section의 주소를 지정하지 않으면, location counter의 현재 값으로부터 그 address가 지정된다. 그러고 나면 location counter는 output section의 size만큼 더해진다. 'SECTIONS'
command를 시작할 때 location counter는 값 '0'
을 갖는다.
두번째 줄은 output section ‘.text’를 define한다. ':'
이 필요하다. '{'
, '}'
안에, output section name 뒤에 이 output section에 두어야 할 input section들의 이름을 나열한다. '*'
는 모든 파일 이름에 매치되는 wildcard이다. 표현식 '*(.text)'
은 모든 input file들 안의 '.text'
input section을 뜻한다.
location counter가 '0x10000'
이었기 때문에, output section '.text'
가 defined될 때, linker는 output file내의 '.text'
section의 주소를 '0x10000'
으로 지정할 거다.
뒤의 줄들은 output file의 '.data'
와 '.bss'
를 define한다. linker는 '.data'
output section을 '0x8000000'
에 둔다. 그러고나면 location counter는 '0x8000000'
+ '.data'
output section의 size가 된다. 그 결과 linker는 '.bss'
output section을 메모리 내에서 '.data'
output section 바로 뒤에 두게 된다.
linker는 필요하다면 location counter를 더해서 각각의 output section에 필요한 alignment를 맞춘다. 이 예제에서는 '.text'
와 '.data'
section들의 지정된 주소들은 아마 alignment constraint를 만족할 것이고 linker는 '.data'
와 '.bss'
section 사이에 작은 갭을 만들어야 했을 수 있다.
이게 다다. 간단하고 완전한 하나의 linker script다.
간단한 Linker Script Command
간단한 linker script command들을 설명한다.
- Entry Point: entry point를 설정
- File Command들: 파일을 다루는 command들
- Format Command들: object file format을 다루는 command들
- REGION ALIAS: memory region에 별칭 붙이기
- 기타 Command들: 다른 linker script command들
Entry point 설정
프로그램 내의 실행되는 첫번째 instruction을 entry point라고 부른다. ENTRY
linker script command로 이 entry point를 설정할 수 있다. 그 argument는 symbol name이다:
ENTRY(symbol)
entry point를 설정하는 여러 방법이 있다. linker는 다음 방법들을 순서대로 실행하면서 entry point를 설정하고, 그 중 하나가 성공하면 멈춘다:
'-e'
entry command-line 옵션;- linker script 안의
ENTRY(symbol)
command; - defined되면, target-specific symbol의 값; 많은 target들에 이는
start
지만, 예를 들면 PE- 그리고 BeOS-based system은 가능한 entry symbols의 list를 check해서 처음 맞는 것을 찾는다. '.text'
section이 있다면 그 첫 byte의 address;- address
0
.
파일을 다루는 command들
여러 linker script command들이 파일을 다룬다.
INCLUDE filename
linker script filename 을 이 지점에 include한다. 현재 디렉토리에서 찾고 나서 -L
옵션으로 지정된 디렉토리 내에서 찾는다. INCLUDE
는 10 레벨까지 중첩 호출이 가능하다.
MEMORY
또는 SECTIONS
commands 안에, 또는 output section descriptions 안에, top level에서 INCLUDE
지시자를 둘 수 있다.
INPUT(file, file, ...)
INPUT(file file ...)
INPUT
command 는 linker에게 그 link 내에 command line 상에서 이름을 정한 것처럼 이름 붙인 file들을 include하도록 지시한다.
예를 들어, 'subr.o'
를 link할 때마다 include하고 싶지만 매번 command line에 이를 입력하고 싶지는 않을 때 'INPUT (subr.o)'
를 linker script 내에 적을 수 있다.
사실 원한다면 linker script 안에 모든 input files를 적어두고, linker를 아무 것도 없이 '-T'
옵션으로 실행할 수 있다.
sysroot prefix가 설정되어 있고, 그 filename이 '/'
character로 시작하고, 처리될 그 script가 sysroot prefix 내부에 있는 경우, 그 filename을 sysroot prefix 내에서 찾을 거다. 아니라면, linker는 먼저 현재 디렉토리 내에서 그 파일을 열 수 있는지 시도해보고, 없으면, archive library search path를 통해 찾는다. 또한 sysroot prefix는 filename path안의 첫번째 character로 =
를 지정하거나 $SYSROOT
를 filename path 앞에 붙임으로써 강제할 수 있다. Command Line Options section 내의 '-L'
의 설명을 한번 보라.
'INPUT (-lfile)'
을 쓰면 ld
는 command line argument '-l'
로 한 것 처럼 libfile.a
로 그 이름을 바꾼다.
implicit linker script 내에서 INPUT
command를 사용하면, 그 파일들은 그 link상의 linker script file이 included된 그 지점에서 include될 것이다. 이는 archive searching에 영향을 준다.
GROUP(file, file, ...)
GROUP(file file ...)
GROUP
command는 named file들이 모두 archive되어야만 한다는 것, 그리고 새로운 undefined reference가 만들어지기 전까지 반복적으로 찾아진다는 것을 제외하면 INPUT
과 비슷하다. Command Line Options section 내의 '-('
의 설명을 보라.
AS_NEEDED(file, file, …)
AS_NEEDED(file file …)
이 construct는 INPUT
이나 GROUP
commands 안에서만 다른 filenames와 나올 수 있다. 나열된 files들은 ELF shared libraries의 그들이 진짜 필요할 때만 추가되는 예외를 제외하면, 그 INPUT
이나 GROUP
commands 안에 직접 나온 것처럼 처리될 거다. 이 construct는 본질적으로 그 안에 나열된 모든 files들에 대해 --as-needed
옵션을 enable하고, --no-as-needed
셋팅 이후에 이전의 --as-needed
를 각각 복구한다.
OUTPUT(filename)
OUTPUT
command는 output file의 이름을 정한다. linker script 안에서 OUTPUT(filename)
을 사용하는 것은 command line (Command Line Options section을 보라) 상에서 '-o filename'
을 사용하는 것과 정확히 같다. 둘 다 사용되면, command line option이 더 우선된다.
OUTPUT command는 보통 기본 값인 'a.out'
말고 다른 file을 기본 값으로 define하려고 할 때 사용할 수 있다.
SEARCH_DIR(path)
SEARCH_DIR
command는 ld
가 library들을 archive할 때 보는 경로의 리스트에 path를 추가한다. SEARCH_DIR(path)
를 사용하는 것은 command line (Command Line Options section을 보라) 상에서 '-L path'
를 사용하는 것과 정확히 같다. 둘 다 사용되면, linker는 두 path 모두 찾는다. command line option에서 지정된 path를 먼저 찾는다.
STARTUP(filename)
STARTUP
command는 command line 상에서 먼저 정해졌던 것처럼 그 filename이 link될 첫 input file이 되는 것을 제외하면 INPUT
command와 똑같다. 이는 entry point가 언제나 첫번째 file의 시작인 시스템을 사용할 때 유용할 거다.
Object file format을 다루는 command들
두 linker script command가 object file format을 다룬다.
OUTPUT_FORMAT(bfdname)
OUTPUT_FORMAT(default, big, little)
OUTPUT_FORMAT
command는 output file을 위해 사용하는 BFD format을 지정한다. (BFD를 보라). OUTPUT_FORMAT(bfdname)
은 command line 상의 '--oformat bfdname'
(Command-line Options를 보라)을 사용하는 것과 정확히 같다. 둘 다 사용되면, command line option이 우선된다.
OUTPUT_FORMAT
을 '-EB'
와 '-EL'
command-line options에 기초하는 다른 foramts을 사용하도록 세 개의 arguments로 사용할 수 있다. 이는 linker script가 원하는 endianness 상의 output format으로 set되도록 한다.
'-EB'
또는 '-EL'
둘 다 사용되지 않으면, output format은 첫번째 argument인 default가 될 거다. '-EB'
가 사용되면 output format은 두번째 argument인 big이 될 거다. '-EL'
이 사용되면 output format은 세번째 argument인 little이 될거다.
예를 들어, MIPS ELF target의 default linker script는 다음 command를 사용한다:
OUTPUT_FORMAT(elf32-bigmips, elf32-bigmips, elf32-littlemips)
이는 그 output file을 위한 그 default format이 'elf32-bigmips'
지만, 사용자가 '-EL'
command-line option을 사용하면 그 output file은 'elf32-littlemips'
format으로 만들어질 것을 이야기한다.
TARGET(bfdname)
TARGET
command는 input file을 읽을 때 사용하는 BFD format의 이름을 지정한다. 이는 이어지는 INPUT
과 GROUP
commands에 영향을 미친다. 이 command는 command line 상의 '-b bfdname'
(Command-line Options를 보라)의 사용과 같다. TARGET
command가 사용되지만 OUTPUT_FORMAT
이 없으면, 마지막 TARGET
command가 그 output file을 위한 format을 set하는데도 사용된다. BFD를 보라.
memory region에 별칭 붙이기
MEMORY command로 생성한 existing memory regions에 별칭을 붙일 수 있다. 각 이름은 하나의 memory region에만 해당한다.
REGION_ALIAS(alias, region)
REGION_ALIAS
function은 memory region region에 별칭 alias를 만든다. 이는 memory regions로의 유연한 output section의 mapping을 할 수 있도록 한다. 다음에 예제가 나온다.
다양한 memory storage devices를 갖고 있는 embedded system을 위한 application이 있다고 가정하다. 뭐든 code 실행 또는 data 저장을 할 수 있는 일반 목적의 volatile memory RAM
을 갖는다. 어떤 것은 code 실행과 read-only data access를 할 수 있는 read-only, non-volatile memory ROM
을 갖을 수 있다. 마지막 variant는 read-only data access와 code 실행을 할 수 없는 read-only, non-volatile memory ROM2
이다. 우리는 4가지 output section을 갖는다:
.text
program code:.rodata
read-only data;.data
read-write initialized data;.bss
read-write zero initialized data.
목적은 output section을 정의하는 system independent한 부분과 그 시스템 상의 available한 memory regions로 output sections를 mapping하는 system dependent한 부분을 포함하는 linker command file을 제공하는 것이다. 우리의 embedded system은 3가지 다른 memory setups A, B 그리고 C로 된다.
Section | Variant A | Variant B | Variant C |
.text | RAM | ROM | ROM |
.rodata | RAM | ROM | ROM2 |
.data | RAM | RAM/ROM | RAM/ROM2 |
.bss | RAM | RAM | RAM |
RAM/ROM
이나 RAM/ROM2
표기는 이 section이 region ROM 이나 region ROM2로 각각 loaded된다는 뜻이다. .data
section의 load address가 .rodata
section의 끝에서 모든 세가지 variants가 시작함에 주의하라.
base linker script는 아래의 output section으로 처리한다. memory layout을 describe하는system dependent한 linkcmds.memory
file을 포함한다.
INCLUDE linkcmds.memory SECTIONS { .text : { *(.text) } > REGION_TEXT .rodata : { *(.rodata) rodata_end = .; } > REGION_RODATA .data : AT (rodata_end) { data_start = .; *(.data) } > REGION_DATA data_size = SIZEOF(.data); data_load_start = LOADADDR(.data); .bss : { *(.bss) } > REGION_BSS }
이제 memory regions들과 별칭을 define하는 세가지 다른 linkcmds.memory
file이 필요하다. 세가지 다른 variants A, B C를 위한 linkcmds.memory
의 내용은 다음과 같다:
A
여기선 모두 RAM
내로 간다.
MEMORY { RAM : ORIGIN = 0, LENGTH = 4M } REGION_ALIAS("REGION_TEXT", RAM); REGION_ALIAS("REGION_RODATA", RAM); REGION_ALIAS("REGION_DATA", RAM); REGION_ALIAS("REGION_BSS", RAM);
B
Program code와 read-only data는 ROM
내로 간다. Read-write data는 RAM
내로 간다. initialized data의 image는 ROM
내에 load되고 system이 시작되는 동안 RAM
내로 copy될 거다.
MEMORY { ROM : ORIGIN = 0, LENGTH = 3M RAM : ORIGIN = 0x10000000, LENGTH = 1M } REGION_ALIAS("REGION_TEXT", ROM); REGION_ALIAS("REGION_RODATA", ROM); REGION_ALIAS("REGION_DATA", RAM); REGION_ALIAS("REGION_BSS", RAM);
C
Program code는 ROM
내로 간다. Read-only data는 ROM2
내로 간다. Read-write data는 RAM
내로 간다. initialized data는 ROM2
내에 load되고, system이 start하는 동안 RAM
내로 copy될 거다..
MEMORY { ROM : ORIGIN = 0, LENGTH = 2M ROM2 : ORIGIN = 0x10000000, LENGTH = 1M RAM : ORIGIN = 0x20000000, LENGTH = 1M } REGION_ALIAS("REGION_TEXT", ROM); REGION_ALIAS("REGION_RODATA", ROM2); REGION_ALIAS("REGION_DATA", RAM); REGION_ALIAS("REGION_BSS", RAM);
필요하다면, ROM
이나 ROM2
로부터 RAM
내로 .data
section을 복사하는 common system initialization routine을 작성할 수 있다.
#include <string.h> extern char data_start []; extern char data_size []; extern char data_load_start []; void copy_data(void) { if (data_start != data_load_start) { memcpy(data_start, data_load_start, (size_t) data_size); } }
다른 linker script command들
다른 linker script command들이 몇 있다.
ASSERT(exp, message)
exp 가 0이 아님을 확인한다. 0이라면, linker가 error code와 message를 찍으면서 종료된다.
모든 assertions들은 linking의 final stage가 시작되기 전에 checked된다. 이는 section definitions 내에 symbols PROVIDE
와 관련된 expression은 user가 이들 symbols의 값을 set하지 않았다면 fail됨을 의미한다. 이 rule의 단 하나의 예외는 .
을 reference하는 PROVIDE
symbol이다. 그래서 다음과 같은 assertion은 __stack_size
가 다른 곳에 defined되지 않았다면 실패한다:
.stack : { PROVIDE (__stack = .); PROVIDE (__stack_size = 0x100); ASSERT ((__stack > (_end + __stack_size)), "Error: No room left for the stack"); }
section definitions 밖의 symbols PROVIDE
는 미리 evaluated되므로 ASSERT
안에서 사용될 수 있다. 그래서 다음은 동작한다:
PROVIDE (__stack_size = 0x100); .stack : { PROVIDE (__stack = .); ASSERT ((__stack > (_end + __stack_size)), "Error: No room left for the stack"); }
EXTERN(symbol symbol ...)
output file에 들어갈 symbol을 undefined symbol로 강제한다. 예를 들면 이는 standard library들로부터 추가 모듈들의 linking을 trigger할 수 있다. 각 EXTERN
마다 여러 symbol들을 나열할 수 있고, EXTERN
은 여러 번 사용할 수 있다. 이 command는 '-u'
command-line option과 같은 효과를 갖는다.
FORCE_COMMON_ALLOCATION
이 command는 '-d'
command-line option과 같은 효과를 갖는다. relocatable output file이 지정 ('-r'
) 되었다 하더라도, common symbol들에 ld
가 space를 assign하도록 만든다.
INHIBIT_COMMON_ALLOCATION
이 command는 ‘–no-define-common’ command-line option과 같은 효과를 갖는다: ld가 non-relocatable output file조차 common symbols로의 address assignment를 생략하게 만든다.
FORCE_GROUP_ALLOCATION
이 command는 ‘–force-group-allocation’ command-line option과 같은 효과를 갖는다: ld가 보통 input sections같은 members를 section group으로 두도록, 그리고 relocatable output file이 지정 ('-r'
) 되었다 하더라도 그 section groups를 delete하도록 만든다.
INSERT [ AFTER | BEFORE ] output_section
이 command는 일반적으로 '-T'
argument로 default SECTIONS으로 예를 들면 overlays로 지정하는 script내에서 사용된다. 이는 output section 후에 (또는 전에) 모두에 우선하는 linker script statements를 삽입하고, 또한 default linker script를 override하지 않도록 '-T'
를 야기한다. 정확한 삽입 지점은 이름 없는 sections을 위한 것과 같다. Location Counter를 보라. 그 삽입은 linker가 output sections들로 input sections들을 mapped한 후에 일어난다. 삽입 전에 ‘-T’ scripts는 default linker script 전에 parsed되고, ‘-T’ script 내의 statements들이 default linker script statements 전에 그 script의 internal linker representation 내에서 일어난다. 특별히 input section assignments는 default script 내의 그것들 전에 ‘-T’ output sections로 만들어질 거다. 여기 ‘-T’ script가 INSERT를 어떻게 사용하는지 볼 수 있는 예제다:
SECTIONS { OVERLAY : { .ov1 { ov1*(.text) } .ov2 { ov2*(.text) } } } INSERT AFTER .text;
NOCROSSREFS(section section ...)
이 command는 ld
에게 어떤 output section들 사이의 모든 reference들에 관한 error를 올리라고 말하는데 사용될 수 있다.
어떤 종류의 program에서, 특히 임베디드 시스템 상에서 overlay가 사용될 때, 한 section이 메모리로 load되면, 다른 section은 아닐 것이다. 두 section 사이의 모든 direct reference는 error가 된다. 예를 들어, 한 section의 code가 다른 section에 defined된 함수를 호출한다면 에러일 거다.
NOCROSSREF
command는 output section 이름의 list를 가져온다. ld
가 section들 사이에 어떤 cross references가 발견하면 error를 보고하고, 0이 아닌 exit status로 return한다. NOCROSSREF
command는 input section name이 아닌 output section name을 사용한다.
NOCROSSREFS_TO(tosection fromsection …)
이 command는 ld
에게 다른 sections의 list로부터 한 section으로의 어떠한 reference라도 error로 올리도록 이야기할 때 쓸 수 있다.
NOCROSSREFS
command는 둘 이상의 output sections이 전부 independent하지만 한 방향의 de[endency가 필요한 경우를 보장할 때 유용하다. 예를 들어 multi-core application에서 각 core로부터 호출될 수 있는 code를 공유하지만 안전을 위해 call back을 사용되지 않아야 할 수 있다.
NOCRESSREFS_TO
command는 output section names의 list를 취한다. 첫번째 section은 다른 어떤 sections로부터도 referenced될 수 없다. ld가 다른 어떤 sections으로부터 첫번째 section으로의 어떤 references라도 탐지하면, error를 report하고 non-zero exist status를 return한다. NOCRESSREFS_TO command는 input section names가 아닌, output section names를 사용함을 주의하라.
OUTPUT_ARCH(bfdarch)
특정 output machine architecture를 지정한다. argument는 BFD library (BFD section을 보라) 에 의해 사용되는 이름 중 하나다. objdump
의 '-f'
옵션을 사용해서 object file의 architecture를 볼 수 있다.
LD_FEATURE(string)
이 command는 ld
의 behavior를 변경하도록 만들 수 있다. string이 “SANE_EXPR”이면 script 내의 absolute symbols와 numbers는 모든 곳에서 간단히 numbers로 취급된다. Expression Section절을 보라.
Symbol에 값 assign하기
linker script 안에서 symbol에 값을 assign할 수 있다. 이는 symbol을 global symbol로 define한다.
간단한 assignment
C assignment operators의 하나를 사용해서 symbol을 assign할 수 있다.
symbol = expression ;
symbol += expression ;
symbol -= expression ;
symbol *= expression ;
symbol /= expression ;
symbol <<= expression ;
symbol >>= expression ;
symbol &= expression ;
symbol |= expression ;
첫번째 경우는 expression의 값으로 symbol을 define한다. 다른 모든 경우는, symbol이 이미 defined되어 있어야 하고, 값이 그에 따라 조정될 것이다.
special symbol name '.'
은 location counter를 나타낸다. SECTIONS
command 내에서만 이를 사용할 거다.
expression 뒤에 ';'
을 꼭 써야 한다.
Expression들은 아래에 defined되어 있다. Linker Script내의 expression 절을 보라.
symbol assignment는 command들의 오른쪽에 command처럼, SECTIONS
command 내의 statement처럼, SECTIONS
command 내의 output section description의 일부처럼 작성될 수 있다.
symbol의 section은 그 expression의 section으로부터 정해진다. Expression의 Section 절을 보라.
여기 symbol assignment가 사용될 수 있는 세가지 다른 곳을 보여주는 예제가 있다.
floating_point = 0; SECTIONS { .text : { *(.text) _etext = .; } _bdata = (. + 3) & ~ 4; .data : { *(.data) } }
이 예제에서 symbol 'floating_point'
는 0으로 defined될 것이다. symbol '_etext'
는 ‘.text’ input section 뒤의 address로 defined될 것이다. symbol '_bdata'
는 ‘.text’ output section 뒤에 4 byte boundary로 올려서 aligned된 address로 defined될 것이다.
HIDDEN
ELF target의 포팅을 위해, 숨겨지고, export되지 않는 symbol을 define하라. syntax는 HIDDEN(symbol = expression)
이다.
여기 간단한 Assignments로부터 HIDDEN을 사용해서 재작성한 예제다:
HIDDEN(floating_point = 0); SECTIONS { .text : { *(.text) HIDDEN(_etext = .); } HIDDEN(_bdata = (. + 3) & ~ 3); .data : { *(.data) } }
이 경우 세 symbol 중 어떤 것도 이 모듈 밖에서 보이지 않을 거다.
PROVIDE
어떤 경우에 어떤 symbol이 reference되긴 하지만 그 link에 포함된 어떤 object에도 defined되어 있지 않을 때 linker script가 그 symbol만 define하고 싶을 수 있다. 예를 들면, symbol 'etext'
가 defined된 traditional linker들 말이다. 그러나 ANSI C는 error 없이 'etext'
를 function name처럼 사용할 수 있어야 함을 요구한다. PROVIDE
keyword는 reference되지만, defined되지는 않는 'etext'
같은 symbol을 define하는데 사용될 수 있다. syntax는 PROVIDE(symbol = expression)
이다.
여기 'etext'
를 define하는 PROVIDE 사용 예제다.
SECTIONS { .text : { *(.text) _etext = .; PROVIDE(etext = .); } }
이 예제에서 프로그램이 '_etext'
를 (앞에 _를 붙여서) define한다면, linker는 multiple definition error가 날 거다. 다르게 말하면, 그 프로그램이 'etext'
를 (앞에 _를 붙이지 않고) define한다면, linker는 프로그램에서 그 definition을 조용히 사용할 것이다. program이 'etext'
를 reference하긴 하지만, define하지는 않았다면, linker는 그 linker script 안의 definition을 사용할 것이다.
주의 – PROVIDE
directive는 그런 symbol이 PROVIDE
가 만드는 symbol과 combined될 수 있다하더라도 defined되는 common symbol로 취급된다. 이는 common symbols로 자주 defined되는 '__CTOR_LIST__'
같은 constructor와 destructor list symbols를 고려할 때 특히 중요하다.
PROVIDE_HIDDEN
PROVIDE
와 비슷하다. ELF target의 포팅을 위해, 그 symbol은 숨겨지고, export되지 않을 거다.
SECTIONS command
SECTIONS
command는 linker에게 input section을 어떻게 output sections으로 map할지, 그리고 output section을 메모리 내에 어떻게 둘지를 알린다.
SECTIONS
command의 형식은 다음과 같다.
SECTIONS { sections-command sections-command ... }
각 sections-command는 다음 중 하나가 될 수 있다.
ENTRY
command (entry point 설정하기 절을 보라)- symbol assignment (Symbol에 값 assign하기)
- output section description
- overlay description
ENTRY
command와 symbol assignment는 편의를 위해 이들 command들 내에 location counter를 사용해서 SECTIONS
command 내에 있을 수 있다. 이는 또한 output file의 layout 내에서 의미 있는 지점에서 이들 command를 사용할 수 있기 때문에 linker script를 이해하기 쉽게 만들어 준다.
Output section description과 overlay description은 아래에서 설명한다.
linker script에서 SECTIONS
command를 사용하지 않으면, linker는 각 input section을 input file에서 처음 나오는 section 순서대로 이름이 같은 output section내에 둘 것이다. 예를 들어, 첫번째 파일 내에 모든 input section들이 있으면 output file 내의 section들의 순서는 첫번째 파일 내의 순서와 같을 거다. 첫 section은 address 0에 있을 것이다.
Output section description
output section의 전체 description은 다음과 같다.
section [address] [(type)] : [AT(lma)] { output-section-command output-section-command ... } [>region] [:phdr :phdr ...] [=fillexp]
대부분의 output section은 거의 대부분의 output section attribute를 사용하지 않는다.
section 앞 뒤로 whitespace가 있어야 하므로 section 이름은 모호하지 않게 된다. ‘:’과 ‘{‘, ‘}’ 또한 있어야 한다. 줄을 바꾸거나 다른 white space는 맘대로 하면 된다.
각 output-section-command는 다음 중 하나일 수 있다.
- symbol assignment (Symbol에 값 assign하기 절을 보라)
- input section description (Input section description 절을 보라)
- 직접 포함하는 data 값 (Output section data 절을 보라)
- special output section keyword (Output section keyword 절을 보라)
Output section 이름
output section의 이름은 section이다. section은 output format에 따라 제약 사항을 만족해야 한다. 적은 수의 section만을 지원하는 format에서는 a.out
같은 이름이 그 format에 의해 지원되는 이름 중의 하나여야 한다(예를 들어, a.out
은 '.text'
, '.data'
또는 '.bss'
만 허용한다). output format이 (Oasys 처럼) 이름이 아닌 숫자로만 모든 section을 지원하면 그 이름은 따옴표로 둘러싼 숫자로된 string으로만 넣어야 한다. section 이름은 문자들로 아무렇게나 구성될 수 있지만 ‘,’같은 일반적인 문자가 있는 이름은 따옴표로 감싸야 한다.
output section 이름 '/DISCARD/'
는 좀 특별하다; Output section 폐기 절 참고
Output section address
address는 output section의 VMA (virtual memory address) 에 대한 expression이다. address를 주지 않으면, linker는 region이 있다면 이를 기준으로 설정하고, 아니면 location counter의 현재 값을 기준으로 설정한다.
address를 주면 output section의 address는 그걸로 정확히 설정된다. address나 region 둘 다 주지 않으면, output section의 address는 location counter의 현재 값을 output section의 alignment 요구사항에 맞춰 align된 값으로 설정된다. output section의 alignment 요구사항은 output section 내에 포함된 모든 input section 중 가장 엄격한 alignment가 적용된다.
예를 들어,
.text . : { *(.text) }
와
.text : { *(.text) }
는 미묘하게 다르다. 첫번째 것은 '.text'
output section의 address를 location counter의 현재 값으로 설정할 것이다. 두번째 것은 location counter의 현재 값이 '.text'
input section의 가장 엄격한 alignment로 aligned된 값으로 설정된다.
address는 임의의 expression이 될 수 있다; Linker Script내의 Expression 절을 참고. 예를 들어, 0x10 boundary 상에 section을 align하길 원하면, 그 section address의 마지막 4 비트는 0이 되고, 다음처럼 하면 된다.
.text ALIGN(0x10) : { *(.text) }
이는 ALIGN
이 현재 location counter를 지정된 값으로 올려서 aligned해서 return하므로 잘 동작한다.
section에 address를 지정하는 것은 location counter의 값을 바꾼다.
Input section description
가장 일반적인 output section command는 input section description이다.
input section description은 가장 기본적인 linker script operation이다. linker에게 메모리 내에 프로그램을 어떻게 lay out할지를 이야기하기 위해 output sections을 사용한다. linker에게 memory layout내로 input file들을 어떻게 map할지를 이야기하기 위해 input section description을 사용한다.
Input section 기본
input section description은 file name으로 구성된다. file name 뒤에는 괄호로 둘러싸인 section name들의 list가 option으로 붙을 수 있다.
file name과 section name은 아래에서 더 설명될 wildcard pattern(Input section wildcard pattern)이 될 수 있다.
가장 일반적인 input section description은 모든 input section을 output section내의 특정 name으로 포함시키는 것이다. 예를 들어, 모든 input '.text'
section을 포함시키려면, 아래처럼 쓸 수 있다:
*(.text)
여기서 '*'
은 모든 file name에 매치되는 wildcard이다.
둘 이상의 section을 포함하는 두가지 방법이 있다.
*(.text .rdata) *(.text) *(.rdata)
이 둘의 차이점은 '.text'
와 '.rdata'
input section이 output section에서 나타날 순서다. 첫번째 예제에서는 이들이 섞인다. 두번째 예제에서는 모든 '.text'
input section이 먼저 나타나고 그 다음에 모든 '.rdata'
input section들이 뒤따른다.
특정 파일로부터 section들을 포함시킬 때 file name을 지정할 수 있다. 하나 또는 그 이상의 file들이 메모리 내의 특정 위치에 있을 필요가 있는 special data를 갖고 있다면 예를 들어 이렇게 쓸 수 있다.
data.o(.data)
section의 list 없이 file name을 사용한다면, input file내의 이들 모든 section들이 output section 안에 포함될 것이다. 이는 보통 하진 않지만 가끔은 유용할 수 있다. 예를 들면 다음과 같다.
data.o
wild card character가 들어있지 않은 file name을 사용하면 linker는 먼저 linker command line이나 INPUT
command 안에 file name이 같이 지정되어 있는지를 먼저 볼 거다. 아니라면 linker는 command line 상에 있을 때처럼 input file로 그 file을 열려고 할거다. 이는 linker가 archive search path에서 그 file을 찾지 않으므로 INPUT
command와는 차이가 있다는 것에 주의해라.
Input section wildcard 패턴
input section description에서 file name 또는 section name 아니면 둘 다 wildcard pattern이 될 수 있다.
많은 예제 내에서 봤던 '*'
의 file name은 file name에 대한 간단한 wildcard pattern이다.
wildcard pattern은 Unix shell에서 사용되는 것과 비슷하다.
'*'
어떤 수의 character들과도 match
'?'
어떤 single character와 match
'[chars]'
chars의 어떤 single instance와 match; '-'
문자는 소문자를 match하기 위한 '[a-z]'
에서처럼 문자의 range를 정하는데 사용된다.
'\'
이 뒤의 다음 character를 quote한다.
file name이 wildcard와 match될 때 wildcard characters는 (Unix 상에서 directory를 나누는데 사용되는) '/'
character와는 match되지 않는다. '*'
character를 포함하는 pattern은 예외다; 이는 '/'
이 들어 있든 말든 항상 어떤 file name과도 match된다. section 이름에서는 wildcard character가 '/'
character와 match된다.
file name wildcard pattern은 command line이나 INPUT
command 상에서 명확히 지정된 file들만 match한다. linker는 wildcard를 확장해서 directory를 찾지 않는다.
어떤 file이 하나 또는 그 이상의 wildcard pattern과 match되거나, 또는 어떤 file이 그 file name이 명확히 나타나면서 하나의 wildcard pattern에도 맞으면, linker는 linker script 내에서 먼저 match되는 것을 사용한다. 예를 들어, 다음 순서의 input section description은 'data.o'
rule이 사용되지 않기 때문에 아마 error일 거다.
.data : { *(.data) } .data1 : { data.o(.data) }
보통 linker는 link하는 동안 보인 순서대로 wildcard에 맞는 file과 section을 둔다. 이를 괄호로 싸인 wildcard pattern 전에 (예를 들면, SORT(.text*)
처럼) SORT keyword를 써서 바꿀 수 있다. SORT keyword를 쓰면 linker는 output file 안에 file과 section을 두기 전에 name으로 오름차순 정렬한다.
input section들이 가는데가 헷갈린다면 map file을 생성하는 '-M'
링커 옵션을 써라. 이 map 파일은 input section이 output section으로 정확히 어떻게 mapped되는지를 보여준다.
다음 예제는 wildcard pattern이 file을 나누는데 어떻게 사용되는지를 보여준다. 이 linker script는 linker에게 '.text'
section을 '.text'
안에, 모든 '.bss'
section을 '.bss'
안에 두도록 지시한다. linker는 대문자로 시작하는 모든 파일로부터의 '.data'
section을 '.DATA'
안에 둘 거다. 다른 모든 파일은 linker가 '.data'
안에 '.data'
를 둘 거다.
SECTIONS { .text : { *(.text) } .DATA : { [A-Z]*(.data) } .data : { *(.data) } .bss : { *(.bss) } }
common symbol에 대한 input section
common symbol을 위해서는 특별한 표기법이 필요하다. 왜냐하면 많은 object file format에서 common symbol들은 특정 input section을 갖지 않기 때문이다. linker는 common symbol들을 그이름이 'COMMON'
인 input section 내에 있는 것처럼 다룬다.
다른 input section들로 하는 것처럼 그냥 'COMMON'
section과 file name을 사용할 수 있다. 다른 input file들로부터의 common symbol들을 또 다른 section 내에 두는 것에 비해 한 section 내에 특정 input file로부터의 common symbol들을 두는데 이를 사용할 수 있다.
대부분의 경우에 input file내의 common symbol들은 output file내의 '.bss'
section내에 두게 된다. 예를 들면:
.bss { *(.bss) *(COMMON) }
어떤 object file format들은 하나 이상의 common symbol 타입을 갖는다. 예를 들면, MIPS ELF object file format은 standard common symbol들과 small common symbol을 구별한다. 이 경우, linker는 다른 common symbol 타입을 위한 다른 특별한 section name과 '.scommon'
을 small common symbol을 위해 사용할 거다. 이는 common symbol의 다른 타입을 메모리의 다른 위치에 map할 수 있도록 한다.
오래된 linker scripts에서는 '[COMMON]'
을 볼 수도 있다. 이 표기는 지금 구식으로 취급된다. 이는 '*(COMMON)'
과 동등하다.
Input section과 garbage collection
link-time garbage collection이 사용중이면 ('--gc-sections'
), 제거되지 말아야할 section들을 표시하는 것이 꽤 유용하다. 이는 KEEP(*(.init))
이나 KEEP(SORT(*)(.ctors))
처럼 input section의 wildcard entry를 KEEP()
으로 둘러쌈으로써 할 수 있다.
Input section 예제
다음 예제는 완전한 linker script이다. 이는 linker에게 file 'all.o'
로부터 모든 section들을 읽어서 그들을 ‘0x10000’위치에서 시작하는 output section 'outputa'
의 시작에 두도록 이야기 한다. 그 바로 뒤에 'foo.o'
file로부터의 section '.input1'
전부가 같은 output section내에 따른다. 'foo.o'
로부터의 section '.input2'
전부는 output section 'outputb'
내로 가고, 그 뒤에 'foo1.o'
로부터의 section '.input1'
이 따른다. 남은 다른 모든 파일로부터의 '.input1'
과 '.input2'
section들 전부가 output section 'outputc'
로 쓰여진다.
SECTIONS { outputa 0x10000 : { all.o foo.o (.input1) } outputb : { foo.o (.input2) foo1.o (.input1) } outputc : { *(.input1) *(.input2) } }
Output section data
output section command로 output section 내에 BYTE
, SHORT
, LONG
, QUAD
또는 SQUAD를
사용해서 byte 수를 정하는 data를 넣을 수 있다. 각 keyword 뒤에는 괄호 안에 저장할 값을 쓴 expression이 따른다 (Linker Scripts안의 Expression절을 보라). expression의 값은 location counter의 현재 값에 저장된다.
BYTE
, SHORT
, LONG과
QUAD
command는 1, 2, 4, 그리고 8 bytes를 (각각) 저장한다. Byte들을 저장하고 나서 location counter는 저장된 byte의 수만큼 증가한다.
예를 들어, 다음은 byte 1을 저장하고 symbol ‘addr’의 4 byte 값이 뒤에 온다:
BYTE(1) LONG(addr)
64 bit host 또는 target을 사용하면 QUAD
와 SQUAD
는 같다; 둘 다 8 byte 또는 64 bit 값을 저장한다. host와 target 모두 32 bit를 사용하면, expression은 32 bit로 계산된다. 이 경우 QUAD는 64 bit로 zero extended된 32 bit 값을 저장하고, SQUAD는 64 bit로 sign extended된 32 bit 값을 저장한다.
output file의 보통의 경우처럼 output file format이 endianness를 명시하면, 그 값은 그 endianness에 따라 저장된다. object file format이 endianness를 명시하지 않으면, 예를 들어 S-records가 true인 경우, 그 값은 첫 input object file의 endianness에 따라 저장된다.
현재 section의 fill pattern을 설정하려면 FILL
command를 사용할 수 있다. 이것 뒤에는 괄호 안의 expression이 온다. 그 section 내의 다른 모든 지정되지 않은 memory regions (예를 들어, input sections에 요구되는 alignment로 인해 남는 gap들)은 그 expression의 two least significant bytes로 필요하면 반복하면서 채워진다. FILL
statement는 그 section definition 내에 나타나는 지점 이후의 memory 위치에 적용된다. 하나 이상의 FILL
statement를 넣으므로써 output section의 다른 부분에 다른 fill pattern을 쓸 수 있다.
다음 예제는 값 '0x9090'
으로 지정되지 않는 메모리 region을 어떻게 채우는지를 보여준다.
FILL(0x9090)
FILL command는 ‘=fillexp‘ output section attribute와 비슷하지만 (Output section 채우기 절을 보라), 전체 section이 아닌, FILL
command 다음의 section 부분에만 영향을 미친다. 둘 다 사용되면 FILL
command가 우선된다.
Output section keywords
output section commands처럼 쓸 수 있는 두가지 keyword가 있다.
CREATE_OBJECT_SYMBOLS
이 command는 linker에게 각 input file을 위한 symbol을 만들라고 이야기한다. 각 symbol의 이름은 해당하는 input file의 이름이 된다. 각 symbol의 section은 CREATE_OBJECT_SYMBOLS
command가 있는 그 output section이 된다. 이는 a.out object file format에 편리하다. 다른 object file에서는 거의 사용되지 않을 것이다.
CONSTRUCTORS
a.out object file format을 사용해서 linking할 때 linker는 C++ global constructors와 destructors를 지원하기 위해 unusual set construct를 사용한다. ECOFF나 XCOFF 같은 arbitrary section을 지원하지 않는 object file format들을 linking할 때 linker는 알아서 그 이름으로 C++ global constructors와 destructors를 인식한다. 이들 object file format들을 위해서 CONSTRUCTORS
command가 linker에게 CONSTRUCTORS
command가 나오는 output section 내에 constructor information을 두도록 알려준다. CONSTRUCTORS command는 다른 object file format들에서는 무시된다. symbol __CTOR_LIST__
는 global constructors의 시작을 표시하고, symbol __DTOR_LIST
는 그 끝을 표시한다. list 내의 첫번째 word는 entries의 수이고, 그 뒤에 각 consructor나 destructor의 address가, 그 뒤에 zero word가 하나 붙는다. compiler는 실제로 code를 실행하기 위헤서 조정을 해야 한다. 이들 object file format들을 위해 GNU C++은 보통 subroutine __main
으로부터 constructors를 호출한다; __main
의 호출은 자동으로 main을 위한 startup code안으로 들어간다. GNU C++은 보통 destructors를 atexit
를 사용하거나 아니면 exit
로부터 그 funtion을 직접 호출해서 실행한다. arbitrary section name을 지원하는 COFF나 ELF 같은 object file format들에는 GNU C++은 .ctors
와 .dtors
sections 안에 global constructors와 destructors의 addresses를 두도록 조정할 것이다. 다음 sequence를 linker script에 두는 것은 GNU C++ runtime code가 보는 table을 build할 것이다.
__CTOR_LIST__ = .; LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2) *(.ctors) LONG(0) __CTOR_END__ = .; __DTOR_LIST__ = .; LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2) *(.dtors) LONG(0) __DTOR_END__ = .;
initialization priority에 global constructors가 실행되는 순서 상의 control을 제공하는 GNU C++ support를 사용하고 있다면, 반드시 정확한 순서로 그들이 실행됨을 보장하기 위해 link time에 constructors를 sort해야 한다. CONSTUCTORS
command를 사용하려면, 대신 'SORT(CONSTRUCTORS)'
를 사용하라. .ctors
와 .dtors
section을 사용하려면, 그냥 '*(.ctors)'
와 '*(.dtors)'
대신에 '*(SORT(.ctors))'
와 '*(SORT(.dtors))'
를 사용하라. 보통 compiler와 linker는 이들 이슈를 알아서 처리할 것이고, 이에 대해 신경쓸 필요가 없을 것이다. 그러나 C++을 사용하고 linker scripts를 작성한다면 이를 고려할 필요가 있을 수 있다.
Output section discarding
linker는 아무 내용도 없는 output section을 만들지 않을 것이다. 이는 어떤 input files내에 있을 수 있는, 혹은 없을 수 있는 input section들을 참조하는데 편리하다. 예를 들어:
.foo { *(.foo) }
는 ‘.foo’ section이 적어도 하나의 input file내에 있을 때만 ‘.foo’ section을 output file 내에 만들거다.
input section description보다는 symbol assignment같은 output section command를 사용한다면, 맞는 input section들이 없어도 output section은 언제나 만들어 질 것이다.
special output section name ‘/DISCARD/’는 input sections을 discard할 때 사용될 수 있다. 이름이 ‘/DISCARD/’인 output section으로 assigned되는 모든 input section들은 output file내에 포함되지 않는다.
Output section attributes
다음처럼 생긴 output section의 full description을 위에서 보았다.
section [address] [(type)] : [AT(lma)] { output-section-command output-section-command ... } [>region] [:phdr :phdr ...] [=fillexp]
이미 section, address, output-section-command에 대해 설명했다. 이번 절에서는 남은 section attributes를 설명할 거다.
Output section type
각 output section은 type을 가질 수 있다. 이 type은 괄호 안의 keyword이다. 다음 type들이 정의된다:
NOLOAD
그 section은 not loadable로 표시되어야 한다. 그래서 프로그램이 실행될 때 memory로 loaded되지 않을 거다.
DSECT
COPY
INFO
OVERLAY
이들 type 이름들은 backward compatibility를 위해서 지원된다. 잘 쓰이진 않는다. 모두 같은 효과를 갖는다. 그 section은 not allocatable로 표시되어야 한다. 그래서 프로그램이 실행될 때 그 section을 위한 memory가 allocated되지 않는다.
linker는 보통 그에 map되는 input section에 기초해서 output section의 attributes를 set한다. section type을 사용해서 이를 override할 수 있다. 예를 들어, 아래의 script sample에서 ‘ROM’ section은 memory location ‘0’에 지정되고, 프로그램이 실행될 때 loaded될 필요는 없다. ‘ROM’ section의 내용은 linker output file내에 평소처럼 나올거다.
SECTIONS { ROM 0 (NOLOAD) : { ... } ... }
Output section LMA
모든 section은 virtual address (VMA)와 load address (LMA)를 갖는다; 기본적인 개념 절을 보라. output section description에 있을 수 있는 address expression은 VMA를 set한다 (Output section address절을 보라).
linker는 보통 LMA를 VMA와 같도록 set한다. 이를 AT
keyword를 사용해서 바꿀 수 있다. AT
keyword 뒤의 expression lma는 그 section의 load address를 지정한다.
이 기능은 ROM image를 쉽게 build하도록 design되었다. 예를 들어, 다음 linker script는 3 output section을 만든다: 0x1000
에서 시작하는 '.text'
, VMA는 0x2000
에서 시작하지만 '.text'
section의 끝에 load되는 '.mdata'
, 그리고, address 0x3000
에 uninitialized data를 잡는 '.bss'
. symbol _data
는 0x2000
으로 정의되는데 이는 location counter가 LMA 값이 아닌 VMA 값을 담는다는 것을 보여준다.
SECTIONS { .text 0x1000 : { *(.text) _etext = . ; } .mdata 0x2000 : AT ( ADDR (.text) + SIZEOF (.text) ) { _data = . ; *(.data); _edata = . ; } .bss 0x3000 : { _bstart = . ; *(.bss) *(COMMON) ; _bend = . ;} }
이 linker script로 generate되는 프로그램을 위한 실제 run-time initialization code는 ROM image로부터 initialized data를 그 runtime address로 복사하기 위해서 다음과 같은 것을 포함할거다. 이 코드가 linker script가 define하는 symbol들을 어떻게 유용하게 쓰는지에 주목하라.
extern char _etext, _data, _edata, _bstart, _bend; char *src = &_etext; char *dst = &_data; /* ROM has data at end of text; copy it. */ while (dst < &_edata) { *dst++ = *src++; } /* Zero bss */ for (dst = &_bstart; dst< &_bend; dst++) *dst = 0;
Output section region
'>region'
을 사용해서 한 section을 이전에 정의된 memory region으로 assign할 수 있다. MEMORY command 절을 보라.
여기 간단한 예제다:
MEMORY { rom : ORIGIN = 0x1000, LENGTH = 0x1000 } SECTIONS { ROM : { *(.text) } >rom }
Output section phdr
':phdr'
을 사용해서 한 section을 이전에 define된 program segment로 assign할 수 있다. PHDRS command 절을 보라. 한 섹션이 하나 또는 그 이상의 segments로 assign되면, 명시적으로 :phdr modifier가 사용되기 전까지 모든 subsequent allocated sections들이 그들 segments로 assign될 것이다. :NONE
으로 linker에게 모든 segment에 그 section을 두지 않을 것임을 알려줄 수 있다.
여기 간단한 예제다:
PHDRS { text PT_LOAD ; } SECTIONS { .text : { *(.text) } :text }
Output section fill
'=fillexp'
를 사용해서 전체 section을 위한 fill pattern을 set할 수 있다. fillexp는 expression이다 (Linker Scripts에서 Expression절을 보라). 그 output section 내의 memory의 모든 unspecified regions (예를 들어, input sections의 required alignment로 인해 남는 gaps) 은 그 값의 2 least significant bytes로 필요하면 반복적으로 채워질거다.
output section commands내에 FILL
command로 fill value를 바꿀 수 있다; Output section data 절을 보라.
여기 간단한 예제다:
SECTIONS { .text : { *(.text) } =0x9090 }
Overlay description
overlay description은 single memory image의 일부로 load되지만 같은 memory address에서 실행되는 section들을 describe하는 쉬운 방법을 제공한다. run time에 어떤 overlay manager가 overlaid sections를 요구사항에 맞게 아마 간단한 addressing bit를 조작해서 runtime memory address의 안과 밖에 copy할 거다. 이 방식은 예를 들면 특정 memory region이 다른 곳보다 빠를 때 유용할 수 있다.
Overlays는 OVERLAY
command를 사용해서 describe한다. OVERLAY
comand는 output section description처럼 SECTIONS
command 안에서 사용된다. OVERLAY command의 full syntax는 다음과 같다:
OVERLAY [start] : [NOCROSSREFS] [AT ( ldaddr )] { secname1 { output-section-command output-section-command ... } [:phdr...] [=fill] secname2 { output-section-command output-section-command ... } [:phdr...] [=fill] ... } [>region] [:phdr...] [=fill]
OVERLAY
(키워드) 를 빼고 모두 옵션이고, 각 section은 name (위에서 secname1과 secname2)을 가져야 한다. OVERLAY
construct 내의 section definitions은 OVERLAY
내에 section들을 위해 define되는 address와 memory regions들이 없다는 것을 제외하면 일반적인 SECTIONS
contruct (SECTIONS command를 보라)내의 것들과 동일하다.
그 sections들은 모두 같은 starting address로 defined된다. 그 sections들의 load addresses는 전체에 OVERLAY
를 위해 사용되는 load address에서 시작하는 memory 내에 consecutive하게 조정된다 (보통 section definitions처럼 load address는 옵션이고 default는 start address이다; start address 또한 옵션이고, default는 현재 location counter 값이다).
NOCROSSREFS
keyword가 사용되면 sections 사이에 어떤 reference가 있다면, linker는 error로 report할 거다. 같은 address에서 section 모두가 실행되면, 보통 한 section이 다른 section에 직접 refer하는 것은 부적절하다. 다른 linker script command 절을 보라.
OVERLAY
내의 각 모든 section에 대해 linker는 자동으로 2개의 symbol을 define한다. symbol __load_start_secname
이 그 section의 starting load address로 defined된다. symbol __load_stop_secname
이 그 section의 final load address로 defined된다. C identifiers내에 있지 않은 secname 내의 모든 문자는 제거된다. C (또는 assembler) code는 이들 symbol을 필요에 따라 overlaid section 근처로 옮기는데 사용할 수 있다.
overlay의 끝에서 location counter의 값은 overlay의 address + 가장 큰 section의 size가 된다.
여기 예제다. SECTION construct 안에 이게 있어야 함을 기억해라.
OVERLAY 0x1000 : AT (0x4000) { .text0 { o1/*.o(.text) } .text1 { o2/*.o(.text) } }
이는 address 0x1000에서 시작하는 '.text0'
와 '.text1'
모두를 define한다. '.text0'
는 address 0x4000에 load되고, '.text1'
은 '.text0'
바로 뒤에 load될 것이다. 다음 symbol들이 defined된다: __load_start_text0
, __load_stop_text0
, __load_start_text1
, __load_stop_text1
.
overlay .text1
을 overlay area로 복사하는 코드는 다음과 같을 것이다.
extern char __load_start_text1, __load_stop_text1; memcpy ((char *) 0x1000, &__load_start_text1, &__load_stop_text1 - &__load_start_text1);
OVERLAY command는 단지 유용한 문법임을 모든 것은 좀 더 많은 basic command를 사용해서 할 수 있음을 알아둬라. 위의 예제는 아래처럼 똑같이 쓰여질 수 있다.
.text0 0x1000 : AT (0x4000) { o1/*.o(.text) } __load_start_text0 = LOADADDR (.text0); __load_stop_text0 = LOADADDR (.text0) + SIZEOF (.text0); .text1 0x1000 : AT (0x4000 + SIZEOF (.text0)) { o2/*.o(.text) } __load_start_text1 = LOADADDR (.text1); __load_stop_text1 = LOADADDR (.text1) + SIZEOF (.text1); . = 0x1000 + MAX (SIZEOF (.text0), SIZEOF (.text1));
MEMORY command
linker의 default configuration은 모든 available memory의 allocation을 허용한다. MEMORY command를 사용해서 이를 override할 수 있다.
MEMORY command는 target 내의 memory block의 location과 size를 describe한다. 이걸로 linker에게 어떤 memory regions들은 사용할지, 어떤 memory regions들은 사용하지 말아야 할지를 describe할 수 있다. 그러고 나면 sections들을 특정 memory regions에 assign할 수 있다. linker는 section addresses를 memory regions에 기초해서 set하고. 너무 꽉 차는 regions들에 대해 경고할 것이다. linker는 available regions내로 맞추기 위해서 section들을 섞지 않는다.
linker script는 최대 하나의 MEMORY command를 사용할 수 있다. 하지만, 원하는 만큼 그 안에 많은 memory의 blocks를 define할 수 있다. 문법은 다음과 같다:
MEMORY { name [(attr)] : ORIGIN = origin, LENGTH = len ... }
name은 linker script 안에서 그 region을 참조하는 이름이다. region name은 linker script 밖에서는 의미가 없다. Region names들은 분리된 name space 안에 저장되고, symbol names, file names, 또는 section names와 conflict이 일어나지 않는다. 각 memory region은 구별되는 name을 가져야 한다.
attr string은 특정 명시적으로 linker script 내에서 mapped되지 않는 input section을 위한 memory region을 사용할지를 지정하는 attributes의 optional list이다. SECTION command 절에서 설명한대로, input section에 대해 output section을 지정하지 않으면, linker는 input section과 같은 이름으로 output section을 만들 것이다. region attributes를 define하면, linker는 생성되는 output section위한 memory region을 선택하는데 그것들을 사용할 거다.
attr string은 다음 characters 중 하나로 구성되어야 한다:
R'</samp>
W’
<p style="padding-left: 40px;">Read-only section</p>
<samp>
Read/write section
X'</samp>
A’
<p style="padding-left: 40px;">Executable section</p>
<samp>
Allocatable section
I'</samp>
L’
<p style="padding-left: 40px;">Initialized section</p>
<samp>
`I’와 같다
`!’
앞에 붙은 attributes 모두를 뒤집기
unmapped section이 '!'
아닌 listed attributes 중 어느 하나에 match되면, memory region내에 두게 된다. '!'
attributes는 이 test를 뒤집는다. 그래서 unmapped section이 listed attributes 중 어느 하나에도 match되지 않으면 그 memory region내에 두게 된다.
origin은 memory region의 start address를 위한 expression이다. 그 expression은 memory allocation을 하기 전에 constant로 evaluate해야 한다. 이는 즉, relative symbol을 사용할 수 없다는 뜻이다. keyword ORIGIN
은 org
또는 o
로 줄여 쓸 수 있다(그러나 ORG같은건 안된다).
len은 memory region의 bytes로 된 size를 위한 expression이다. origin expression과 같이 쓰면, 그 expression은 memory allocation을 하기 전에 constant로 evaluate해야 한다. keyword LENGTH
는 len
이나 l
로 줄여 쓸 수 있다.
다음 예제에서 allocation을 위해 available한 2개의 memory regions이 있다고 지정한다: 하나는 '0'
에서 시작하는 256 kilobytes 하나, 4 megabytes인 '0x40000000'
에서 시작하는 다른 하나. linker는 명시적으로 memory region 내로 mapped하지 않는, read-only 또는 excutable 중 하나인 모든 section울 'rom'
memory region 내로 둘 거다. linker는 명시적으로 memory region내로 mapped하지 않는 다른 sections들을 'ram'
memory region 내에 둘 것이다.
MEMORY { rom (rx) : ORIGIN = 0, LENGTH = 256K ram (!rx) : org = 0x40000000, l = 4M }
memory region을 define하고 나면, '>region'
output section attributes를 사용해서 linker에게 그 memory region내로 지정한 output section을 두도록 지시할 수 있다. 예를 들어, 'mem'
이라는 memory region이 있으면, 그 output section definition 내에 '>mem'
를 사용할 수 있다. Output section region 절을 보라. output section에 아무 address도 지정되어 있지 않으면, linker는 그 memory region 내의 다음 available address로 그 address를 set할 거다. 한 memory region으로 direct된 combined output section이 그 region에 비해 너무 크면, linker는 error message를 올릴 거다.
PHDRS command
ELF object file format은 segments로 알려진 program headers를 사용한다. program headers는 어떻게 program이 memory 내로 load되어야 하는지를 describe한다. objdump 프로그램에 ‘-p’ 옵션을 사용해서 이를 출력할 수 있다.
native ELF system 상에서 ELF program을 실행하면, system loader는 그 program을 load할지를 살펴보기 위해서 program headers를 읽는다. 이는 그 program header가 맞게 set되어있어야만 동작한다. 이 매뉴얼은 어떻게 system loader가 program header를 interpret하는지 세부사항은 describe하지 않는다; 더 자세히는 ELF ABI를 보라.
linker는 default로 reasonable한 program headers를 생성한다. 그러나 어떤 경우에 더 세부적인 program headers를 지정할 필요가 있을 수 있다. 이 목적으로 PHDRS command를 사용할 수 있다. linker가 PHDRS command를 linker script안에서 보면, 지정된 것 외에 어떤 program headers도 만들지 않을 거다.
linker는 ELF output file을 generate할 때만 PHDRS command를 신경쓴다. 다른 때는 PHDRS를 그냥 무시할거다.
PHDRS command의 문법은 다음과 같다. words PHDRS, FILEHDR, AT 그리고 FLAGS가 keywords이다.
PHDRS { name type [ FILEHDR ] [ PHDRS ] [ AT ( address ) ] [ FLAGS ( flags ) ] ; }
name은 linker script의 SECTIONS
command 내에서 참조를 위해서만 사용된다. 이를 output file내에 두지는 않는다. Program header names은 분리된 name space에 저장되고, symbol names, file names, 또는 section names와 conflict이 일어나지 않는다. 각 program header는 구별되는 이름을 가져야 한다.
특정 program header types들은 system loader가 file로부터 load할 memory의 segments를 describe한다. linker script내에서 그 segments내에 allocatable output sections를 둠으로써 이들 segments의 contents를 지정한다. 특정 segment내에 section을 두는데는 ':phdr'
output section attribute를 사용한다. Output section phdr 절을 보라.
하나 이상의 segment 내에 특정 sections들을 두는 것이 보통이다. 이는 그냥 다른 것을 하나의 memory segment가 포함함을 의미한다. section을 포함할 각 segment마다 한번씩 사용해서 ':phdr'
를 반복할 수 있다.
':phdr'
를 사용해서 하나 이상의 segments 내에 한 section을 두면, linker는 같은 segments 내에 ':phdr'
을 지정하지 않은 모든 이어지는 allocatable sections들을 둘 거다. 이는 일반적으로 single segment 내에 contiguous sections 전체를 두기 때문에 편의를 위한 것이다. default segment를 override하기 위해서, 그리고 linker에게 어떤 segment에도 그 section을 전혀 두지 않겠다고 알리기 위해서 :NONE
을 사용할 수 있다.
segment의 contents를 더 많이 describe하기 위해서 program header type 뒤에 FILEHDR
와 PHDRS
keyword를 사용할 수 있다. FILEHDR
keyword는 그 segment가 ELF file header를 포함해야 함을 의미한다. PHDRS
keyword는 그 segment가 ELF program headers 그 자체를 포함해야 함을 의미한다.
type은 다음 중 하나일 수 있다. 숫자는 keyword의 값을 나타낸다.
PT-NULL (0)
unused program header를 나타냄.
PT_LOAD (1)
이 program header는 file로부터 load되는 segment를 describe함을 나타냄.
PT_DYNAMIC (2)
dynamic linking information이 있을 수 있는 segment를 나타냄
PT_INTERP (3)
program interpreter의 name이 있을 수 있는 segment를 나타냄
PT_NOTE (4)
note information을 담고 있는 segment를 나타냄.
PT_SHLIB (5)
ELF ABI에 의해 지정되지 않지만 defined된 reserved program header type
PT_PHDR (6)
program header가 있을 수 있는 segment를 나타냄
expression
program header의 numeric type을 주는 expression. 이는 위에서 defined되지 않은 type을 위해 사용될 수 있음.
AT
expression을 사용해서 memory 내의 특정 address에 load되어야 하는 segment를 지정할 수 있다. 이는 output section attribute로 사용되는 AT
command와 완전히 같다 (Output section LMA 절을 보라). program header를 위한 AT
command는 output section attribute를 override한다.
linker는 보통 segment를 구성하는 section에 기초해서 segment flags를 set할거다. segment flags를 명시적으로 지정하기 위해서 FLAGS
keyword를 사용할 수 있다. flags의 값은 integer여야 한다. 이는 program header의 p_flags
field를 set하는데 사용될 수 있다.
여기 PHDRS
의 예제다. 이는 native ELF system 상에서 사용되는 program header의 전형적인 set을 보여준다.
PHDRS { headers PT_PHDR PHDRS ; interp PT_INTERP ; text PT_LOAD FILEHDR PHDRS ; data PT_LOAD ; dynamic PT_DYNAMIC ; } SECTIONS { . = SIZEOF_HEADERS; .interp : { *(.interp) } :text :interp .text : { *(.text) } :text .rodata : { *(.rodata) } /* defaults to :text */ ... . = . + 0x1000; /* move to a new page in memory */ .data : { *(.data) } :data .dynamic : { *(.dynamic) } :data :dynamic ... }
VERSION command
(생략)
Linker Scripts내의 Expression
linker script language 내의 expression의 syntax는 C expression의 그것과 완전히 같다. 모든 expressions는 integer로 evaluated된다. 모든 expression은 host와 target이 모두 32 bits면 32 bits로, 아니면 64 bits로, 모두 같은 size로 evaluated된다.
expressions내의 symbol values를 set하고 사용할 수 있다.
linker는 expression내의 사용을 위해 여러 특별 목적의 builtin functions을 define한다.
- Constants: Constants
- Symbols: Symbol Names
- Location Counter: Location Counter
- Operators: Operators
- Evaluation: Evaluation
- Expression Section: Expression의 Section
- Builtin Functions: Builtin Functions
Constants
모든 constants는 integers다.
C에서처럼 linker는 '0'
으로 시작하는 integer는 8진수이고, '0x'
또는 '0X'
로 시작하는 integer는 16진수다. linker는 다른 integers는 10진수로 생각할 거다.
추가로 suffixes K
와 M
을 각각 constant의 scale로 사용할 수 있다. 예를 들어 다음 모두는 같은 값을 참조한다:
_fourk_1 = 4K; _fourk_2 = 4096; _fourk_3 = 0x1000;
Symbol Names
quoted되지 않는 한 symbol name은 letter, '_'
, 또는 '.'
으로 시작하고, letters, digits, '_'
, '.'
, '-'
을 포함할 수 있다. Unquoted symbol names는 어떤 keywords와도 conflict되지 않아야 한다. odd characters를 포함하거나 keyword와 같은 이름을 가진 symbol을 double quoted로 둘러싸서 지정할 수 있다:
"SECTION" = 9; "with a space" = "also with a space" + 10;
symbol이 많은 non-alphabetic characters를 포함할 수 있기 때문에, spaces로 symbol들을 구분하는게 안전하다. 예를 들어, ‘A – B’는 뻴셈 expression이지만, ‘A-B’는 하나의 symbol이다.
Location Counter
특별한 linker variable dot '.'
은 언제나 현재 output location counter를 담는다. .
이 언제나 output section 내의 한 위치를 가리키므로, 이는 SECTION command 안의 expression 에서만 보일 수 있다. .
symbol은 expression 내에 보통 symbol이 있을 수 있는 곳 어디에나 있을 수 있다.
.
에 값을 assign하는 것은 location counter를 옮기게 한다. 이는 output section에 holes를 만들 때 사용될 수 있다. location counter는 뒤로 옮겨지진 않을 거다.
SECTIONS { output : { file1(.text) . = . + 1000; file2(.text) . += 1000; file3(.text) } = 0x1234; }
위의 예제에서 'file1'
으로부터의 '.text'
section은 output section 'output'
의 시작에 위치한다. 그 뒤에 1000 byte gap이 붙는다. 그리고 'file2'
로부터의 '.text'
section이 나오고, 또 1000 byte gap이 'file3'
로부터의 '.text'
앞에 붙는다. '= 0x1234'
표기는 gap들에 쓰여질 data를 지정한다 (Output section fill 절을 보라).
Operators
linker는 표준 C의 arithmetic operators를 같은 standard binding과 우선순위 레벨로 인식한다:
우선순위 associativity Operators Notes (highest) 1 left ! - ~ (1) 2 left * / % 3 left + - 4 left >> << 5 left == != > < <= >= 6 left & 7 left | 8 left && 9 left || 10 right ? : 11 right &= += -= *= /= (2) (lowest)
Notes: (1) Prefix operators (2) Assignments를 보라
Evaluation
linker는 lazy하게 expression을 evaluate한다. 전적으로 필요할 때만 expression의 값을 계산한다.
linker는 어떤 linking이든 전부 하기 위해서 첫번째 section의 start address 값, memory regions의 origin과 길이 같은 information을 필요로 한다. 이들 값은 linker가 linker scripts내에서 읽는대로 바로 계산된다. 그러나 (symbol values 같은) 다른 값들은 storage allocation 후까지 필요하지 않거나 모른다. 이런 값들은 나중에 (output section의 size 같은) 다른 information이 symbol assignment expression 내에서 사용되기 위해 available할 때 evaluated된다.
sections의 size는 allocation 이후까지 알 수 없기 때문에 assignments는 allocation 이후까지 수행되지 않는 것에 의존한다.
location counter '.'
에 의존하는 것 같은 어떤 expression은 section allocation 중에 evaluated되어야 한다.
expression의 결과가 필요하지만, 그 값이 available하지 않으면 error가 난다. 예를 들어 다음과 같은 script는
SECTIONS { .text 9+this_isnt_constant : { *(.text) } }
error message `non constant expression for initial address'
를 내게 한다.
Expression의 Section
linker가 expression을 evaluate하면 그 결과는 absolute이거나 어떤 section에 relative하거나 둘 중 하나다. relative expression은 section의 base로부터 fixed offset으로 표현된다.
linker script내의 expression의 위치가 그게 absolute인지 relative인지를 결정한다. output section definition 내에 있는 expression은 그 output section의 base에서 relative이다. 다른 곳에 있는 expression은 absolute가 된다.
'-r'
옵션을 사용해서 relocatable output을 요청하면 relative expression으로의 symbol set은 relocatable일 거다. 이는 그 이상의 link operation이 symbol의 값을 바꿀 수 있음을 의미한다. 그 symbol의 section은 relative expression의 그 section이 될 거다.
absolute expression으로의 symbol set은 모든 그 이상의 link operation을 통해 같은 값으로 유지될 거다. 그 symbol은 absolute가 될 거고 다른 특정 연관된 section을 갖지 않을 거다.
builtin function ABSOLUTE
를 사용해서 지정하지 않았다면 relative가 될 때, 그 expression이 absolute가 되도록 강제할 수 있다. 예를 들어 output section '.data'
의 end address로 absolute symbol set을 만드려면:
SECTIONS { .data : { *(.data) _edata = ABSOLUTE(.); } }
'ABSOLUTE'
가 쓰이지 않았다면 '_edata'
는 '.data'
section의 relative가 됐을 거다.
Builtin Functions
linker script language는 linker script expression 내에 쓸 수 있는 많은 builtin function을 갖고 있다.
ABSOLUTE(exp)
expression exp의 absolute (non-negative와 반대인 non-relocatable) 값을 return. section definition 내에서 symbol 값이 보통 relative가 되는 데서 symbol로 absolute 값을 assign하는데 주로 유용함. Expression 절을 보라.
ADDR(section)
section 이름의 address (VMA)를 return. 사용 전에 그 section의 location을 defined했어야 한다. 다음 예제에서 symbol_1
이 .output1
section의 relative가 되고 다른 둘은 absolute가 되는 것을 제외하면 start_of_output1
, symbol_1
과 symbol_2
는 동등한 값이 assigned됨:
SECTIONS { … .output1 : { start_of_output_1 = ABSOLUTE(.); … } .output : { symbol_1 = ADDR(.output1); symbol_2 = start_of_output_1; } … }
ALIGN(align)
ALIGN(exp,align)
다음 align
boundary로 aligned된 location counter (.
) 나 임의의 expression을 return. operand가 하나인 ALIGN
은 location counter의 값을 바꾸지 않는다. 그 값에 arithmetic을 할 뿐이다. operand가 둘인 ALIGN
은 임의의 expression이 위로 aligned되도록 할 수 있다 (ALIGN(align)
은 ALIGN(ABSOLUTE(.), align)
과 동등하다).
여기 앞의 section 이후에 output .data
section을 다음 0x2000
byte boundary로 align하고, input section 이후에 다음 0x8000
boundary로의 section 내에 variable을 set하는 예제다:
SECTIONS { ... .data ALIGN(0x2000): { *(.data) variable = ALIGN(0x8000); } ... }
이 예제에서의 ALIGN
의 첫번째 용도는 section definition의 optional address attribute로 사용되었기 때문에 section의 위치를 지정하는 거다 (Output Section Address절을 보라). ALIGN
의 두번째 용도는 symbol의 값을 define하는데 사용하는 거다.
builtin function NEXT
가 ALIGN
과 매우 관련이 있다.
ALIGNOF(section)
그 section이 allocated되었다면, section이 이름인 것의 bytes로의 alignment를 return. 그 section이 evaluated될 때 allocated되지 않았다면, linker는 error를 report할거다. 다음 예제에서 .output
section의 alignment가 그 section내의 첫번째 값으로 저장된다.
SECTIONS{ … .output { LONG (ALIGNOF (.output)) … } … }
BLOCK(exp)
이는 오래된 linker script의 compatibility를 위한 ALIGN
의 다른 이름이다. output section의 address를 setting할 때 자주 보인다.
DATA_SEGMENT_ALIGN(maxpagesize, commonpagesize)
이는 다음 둘 중 하나와 동등하다.
(ALIGN(maxpagesize) + (. & (maxpagesize - 1))) 또는 (ALIGN(maxpagesize) + ((. + commonpagesize - 1) & (maxpagesize - commonpagesize)))
뒤의 것이 (이 expression의 결과와 DATA_SEGMENT_END
사이의 area인) data segment를 위해 더 작은 commonpagesize size의 pages를 사용하는지에 따라 앞의 것인지 아닌지가 달려 있다. 뒤의 form이 사용되면, runtime memory의 commonpagesize bytes가 on-disk file 내의 최대 commonpagesize로 낭비되는 bytes까지 포함해서 save될 것임을 의미한다.
이 expression은 SECTIONS
command들 내에 직접 사용될 수 있지만, 모든 output section description에는 있을 수 없고, linker script에 딱 한번만 나올 수 있다. 최대 maxpagesize system page sizes 상에서 실행되는 동안, commonpagesize는 maxpagesize 이하여야 하고, object가 optimized되기를 원하는 system page size여야 한다. 그러나 system page size가 commonpagesize보다 크면 '-z relro'
protection이 영향을 미치지 않음을 주의하라.
예제:
. = DATA_SEGMENT_ALIGN(0x10000, 0x2000);
DATA_SEGMENT_END(exp)
이는 DATA_SEGMENT_ALIGN evaluation 목적의 data segment의 끝을 define한다.
. = DATA_SEGMENT_END(.);
DATA_SEGMENT_RELRO_END(offset, exp)
이는 '-z relro'
옵션을 사용했을 때 PT_GNU_RELRO
segment의 끝을 define한다. '-z relro'
옵션이 없으면, DATA_SEGMENT_RELRO_END
가 아무것도 하지 않지만, 다른 때는 DATA_SEGMENT_ALIGN
이 padded되어서 DATA_SEGMENT_ALIGN
에 주어진 commonpagesize argument로 exp + offset이 aligned된다. linker script내에 두려면, DATA_SEGMENT_ALIGN과 DATA_SEGMENT_END 사이에 이를 두어야 한다. 두번째 argument로 인한 PT_GNU_RELRO
segment의 끝에서 두번째 argument + 필요한 모든 padding을 evaluate하라.
. = DATA_SEGMENT_RELRO_END(24, .);
DEFINED(symbol)
symbol이 linker global symbol table안에 defined되어 있으면 1을, 아니면 0을 return. symbol의 default values를 제공하는데 이 function을 사용할 수 있다. 예를 들어, 다음 script 조각은 global symbol 'begin'
을 '.text'
section 내의 첫번째 위치로 어떻게 set하는지를 보여준다–그러나 'begin'
이란 symbol이 이미 있다면, 그 값이 유지된다:
SECTIONS { ... .text : { begin = DEFINED(begin) ? begin : . ; ... } ... }
LENGTH(memory)
이름이 memory인 memory region의 length를 return
LOADADDR(section)
이름 section의 absolute LMA를 retrun
LOG2CEIL(exp)
무한 쪽으로 올림된 exp의 밑이 2인 log. LOG2CEIL(0)는 0을 return
MAX(exp1, exp2)
exp1과 exp2 중 더 큰 걸 return
MIN(exp1, exp2)
exp1과 exp2 중 더 작은 걸 return
NEXT(exp)
exp의 multiple인 다음 unallocated address를 return. 이 함수는 ALIGN(exp)
와 매우 관련이 깊다; output file의 discontinuous memory를 define하는데 MEMORY
command를 사용하기 전까지, 두 functions은 동등하다.
ORIGIN(memory)
이름이 memory인 memory region의 origin을 return
SEGMENT_START(segment, default)
이름이 segment인 것의 base address를 return. 이 segment에 명시적인 값이 (command-line '-T'
option으로) 주어지지 않았다면 그 값이 return되고, 아니면 그 값은 default가 될거다. 현재, '-T'
command-line option은 “text”, “data”, 그리고 “bss” section의 base address를 set하는데만 사용될 수 있지만, 어떤 segment name이든 SEGMENT_START
를 사용할 수 있다.
SIZEOF(section)
그 섹션이 allocated되었다면, section이 이름인 것의 bytes로 된 size를 return. 그 section이 evaluate될 때 allocated되지 않았다면, linker는 error를 report할 거다. 다음 예제에서, symbol_1
과 symbol_2
는 같은 값이 assigned된다.
SECTIONS{ … .output { .start = . ; … .end = . ; } symbol_1 = .end - .start ; symbol_2 = SIZEOF(.output); … }
SIZEOF_HEADERS
sizeof_headers
output file의 header의 bytes로 된 size를 return. 이는 output file의 시작에서 나오는 information이다. paging이 가능하도록 골랐다면, 첫번째 section의 start address를 setting할 때 이 수를 사용할 수 있다.
ELF output file을 생성할 때 linker script가 SIZEOF_HEADERS
builtin function을 사용하면, linker는 모든 section addresses와 sizes를 결정하기 전에 program headers의 수를 계산해야 한다. linker가 additional program headers가 필요함을 발견하면, error ‘not enough room for program headers’를 report할 거다. 이 에러를 피하려면, SIZEOF_HEADERS function의 사용을 피하거나, linker가 additional program headers를 강제로 사용하는 것을 피하도록 linker script를 재작성하거나, PHDRS
command를 사용해서 (PHDRS를 보라) program headers 자체를 define해야 한다.
Implicit Linker Scripts
linker script로 linker가 인식할 수 없는 object file이나 archive file을 지정하면, linker script로 그 파일을 읽으려고 시도할거다. 그 file이 linker script로 parsed될 수 없으면, linker는 error를 report할 거다.
Implicit linker script는 default linker script를 대신하지 않는다.
일반적으로 implicit linker script는 symbol assignment, 또는 INPUT
, GROUP
이나 VERSION
commands만 포함할거다.
implicit linker script는 implicit linker script가 read된 command line의 위치에서 read되므로 어떤 input files이든 읽는다. 이는 archive searching에 영향을 준다.
[Python] 정규식 사용법
저자: A.M. Kuchling <amk@amk.ca>
원문: Regular Expression HOWTO (https://docs.python.org/ko/3.7/howto/regex.html)
번역: 양정석 <dasomoli at gmail.com>
요약
이 문서는 re
모듈로 파이썬에서 정규식(regular expression)을 사용하는 것에 대한 입문 튜토리얼입니다. 라이브러리 레퍼런스의 해당 절보다 좀 더 친절한 소개를 제공합니다.
소개
정규식 (REs, 또는 regexecs, 또는 regex patterns라고 부릅니다) 들은 원래 작은, 파이썬 내부에 내장된 매우 특별화된 프로그래밍 언어이고, re 모듈을 통해 이용 가능하도록 만들어졌습니다. 이 작은 언어를 사용해서 여러분은 여러분이 맞추길 원하는 가능한 문자열 집합의 규칙을 정합니다; 이는 영어 문장, 또는 E-mail 주소, 또는 TeX 명령, 또는 원하는 어떤 것이든 포함할 수 있습니다. 그럼 여러분은 “이 문자열이 이 패턴에 맞을까?” 또는 “이 문자열 어딘가에 이 패턴에 맞는 게 있을까?”와 같은 질문을 할 수 있습니다. 여러분은 또한 다양한 방법으로 문자열을 편집하거나, 부분으로 나누거나 하는데 RE를 사용할 수 있습니다.
정규식 패턴들은 C로 작성된 매칭 엔진에 의해 연속된 바이트코드로 컴파일된 후 실행됩니다. 고급 용도를 위해, 주어진 RE를 엔진이 어떻게 실행할지에 조심스럽게 주의를 기울이거나 더 빨리 실행되는 바이트코드를 만들기 위해 정해진 방법으로 RE를 작성할 필요가 있을 수 있습니다. 최적화는 매칭 엔친의 내부를 잘 알 필요가 있기 때문에 이 문서에서 다루지 않습니다.
정규식 언어는 상대적으로 작고 제한적이라서 정규식을 사용해서 모든 문자열 처리 작업이 가능하진 않습니다. 정규식으로 할 수 있지만, 그 표현식이 너무 복잡한 작업도 있습니다. 이런 경우, 그런 처리를 위한 파이썬 코드를 작성하는 것이 더 나을 수도 있습니다; 파이썬 코드가 정교한 정규식보다는 느리겠지만, 아마 더 이해하긴 쉬울 겁니다.
간단한 패턴
우리는 가장 간단한 정규식을 배우는 것으로 시작할 겁니다. 정규식이 문자열을 처리하는데 사용되므로, 우리는 가장 일반적인 작업으로 시작할 겁니다; 문자 맞추기.
정규식에 깔려 있는 컴퓨터 과학의 자세한 설명(결정적과 비결정적 유한 오토마타)을 위해서는, 컴파일러 작성에 관한 거의 대부분의 아무 교과서나 참고할 수 있습니다.
문자 맞추기
대부분의 글자나 문자들은 간단히 그들 스스로와 맞을 것입니다. 예를 들어, 정규식 test 는 정확히 문자열 test 와 맞습니다. (여러분은 이 RE 가 Test 또는 TEST와 잘 맞도록 대소문자 구별없는 모드를 켤 수 있습니다; 나중에 더 보죠).
이 룰에는 예외가 있습니다; 어떤 문자들은 특별한 메타문자들이고, 그들 자체로 맞추지 않습니다. 대신에, 그들은 기존의 것이 아닌 것과 맞춰질 거라거나 RE의 다른 부분에 그들이 반복되거나 그들의 의미가 바뀌는 것에 의해 영향을 줄거라는 신호를 줍니다. 이 문서의 상당부를 다양한 메타문자들에 대해 이야기하고 그들이 무엇을 하는지에 대해 쓸 겁니다.
여기 메타문자들의 전체 목록입니다; 그들의 의미는 이 사용법의 나머지에서 이야기 될 겁니다.
. ^ $ * + ? { } [ ] | ( )
우리가 볼 첫 메타문자는 [
와 ]
입니다. 그들은 여러분이 맞추길 원하는 문자들의 집합인 문자 클래스를 지정하는데 사용됩니다. 문자들은 개별적으로 나열될 수도 있고, 두 문자와 그 둘을 나누는 '-'
로 범위를 나타낼 수도 있습니다. 예를 들면, [abc]
는 a
, b
, 나 c
문자 중 하나와 맞을 겁니다; 이는 문자의 같은 집합을 표현하는 범위를 사용하는 [a-c]
와 같습니다. 여러분이 소문자만 맞추길 원했다면, 여러분의 RE는[a-z]
가 됩니다.
메타문자들은 클래스 안에서는 효력이 없습니다. 예를 들어, [akm$]
는 'a'
, 'k'
, 'm'
또는 '$'
중 어느 하나와 맞을 겁니다. '$'
는 보통 메타문자입니다만, 문자 클래스 안에서는 그 뜻을 잃게 됩니다.
여러분은 집합을 뒤집음으로써 클래스 안에 나열되지 않은 문자들을 맞출 수 있습니다. 이는 클래스의 첫문자로 '^'
를 넣는 것으로 나타냅니다. 예를 들어, [^5]
는 '5'
를 제외한 다른 글자와 맞춰질 겁니다. 캐럿이 문자 클래스 안의 다른 곳에 있다면, 특별한 뜻이 없습니다. 예를 들어: [5^]
는 '5'
나 '^'
중 하나와 맞춰질 겁니다.
아마 가장 중요한 메타문자는 백슬래시 일겁니다. 파이썬의 문자 리터럴에서처럼, 백슬래시는 뒤에 붙는 다양한 문자로, 다양하고 특별한 문자 연속을 표시할 수 있습니다. 이는 또한 모든 메타문자들을 예외 처리하는데 사용되어서 여러분은 여전히 패턴 안에서 그들을 맞출 수 있습니다; 예를 들어, 여러분이
[
나 를 맞출 필요가 있다면, 여러분은 그들의 특별한 의미를 없애기 위해서 그들 앞에 이걸 붙일 수 있습니다;
[
나 \
.
''
로 시작하는 특별한 문자 연속들 일부는 숫자의 집합, 글자의 집합, 공백 문자가 아닌 다른 것들의 집합처럼 유용한 미리 정의된 문자들의 집합을 나타냅니다.
예를 한번 보죠: w
는 어떤 글자와 숫자로 된 문자와 맞춰집니다. regex 패턴이 바이트들로 표현된다면, 이는 클래스 [a-zA-Z0-9_]
와 동등합니다. regex 패턴이 문자열이라면, w
는 unicodedata 모듈에서 제공하는 유니코드 데이터베이스 안의 문자로 표시된 모든 문자와 맞춰질 겁니다. 여러분은 정규식을 컴파일할 때 re.ASCII 플래그를 줌으로써 문자열 패턴 안의 w
의 좀 더 제한적인 정의를 사용할 수도 있습니다.
다음 특별한 문자 연속의 목록은 완전하진 않습니다. 문자 연속의 완전한 목록과 유니코드 문자열 패턴의 확장된 클래스 정의를 위해서는, 표준 라이브러리 레퍼런스의 정규식 문법의 마지막 부분을 보세요. 보통 유니코드 버전은 유니코드 데이터베이스 안의 적절한 분류 내에 있는 어떤 문자와 맞춰집니다.
d
10진수 숫자와 맞춰짐; 클래스 [0-9]
와 동등.
D
10진수가 아닌 다른 문자와 맞춰짐; 클래스 [^0-9]
와 동등.
s
공백 문자와 맞춰짐; 클래스 [ tnrfv]
와 동등.
S
공백 문자가 아닌 문자와 맞춰짐; 클래스 [^ tnrfv]
와 동등.
w
글자와 숫자로 된 문자와 맞춰짐; 클래스 [a-zA-Z0-9_]
와 동등.
W
글자와 숫자가 아닌 문자와 맞춰짐; 클래스 [^a-zA-Z0-9_]
와 동등.
이들 문자 연속은 문자 클래스 안에 포함될 수 있습니다. 예를 들어, [s,.]
는 공백 문자나 ','
또는 '.'
과 맞춰지는 문자 클래스입니다.
이 절의 마지막 메타문자는 . 입니다. 이는 개행 문자를 제외한 나머지와 맞춰집니다. 그리고 하나의 개행 문자일 때도 맞추는 다른 모드 (re.DOTALL) 도 있습니다. .
은 여러분이 “아무 문자”나 맞추길 원할 때 종종 쓰입니다.
반복되는 것
정규식은 문자열에서 이용가능한 메소드들로 할 수 없는 것을 문자의 다양한 집합과 맞출 수 있도록 되는 것이 첫번째 입니다. 그러나 그것만 regexes의 부가적인 능력이었다면, 그들이 이렇게 많이 발전하지 않았을 겁니다. 다른 능력은 RE의 일부가 정해진 횟수만큼 반복될 것임을 지정할 수 있다는 겁니다.
우리가 볼 반복되는 것들을 위한 첫 메타문자는 *
입니다. *
는 리터럴 문자 '*'
와 맞지 않습니다; 대신, 이전 문자가 정확히 한번 대신, 0번 또는 그 이상 맞춰질 수 있음을 지정합니다.
예를 들어, ca*t
는 'ct'
(0 'a'
문자), 'cat'
(1 'a'
), 'caaat'
(3 'a'
문자들), 이렇게요.
*
와 같은 반복은 그리디(greedy)합니다; 하나의 RE가 반복될 때, 매칭 엔진은 가능한한 많이 반복하도록 하려 할 겁니다. 패턴의 나중 부분이 맞지 않으면, 매칭 엔진은 그럼, 뒤로 돌아가서 좀 더 적은 반복으로 다시 시도해 볼 겁니다.
단계별 예제는 이를 좀 더 명확히 할 겁니다. 표현식 a[bcd]*b
를 보죠. 이것은 글자 'a'
, 클래스 [bcd]
로부터의 0번 또는 그 이상의 문자들, 그리고 마지막에 'b'
로 끝나는 것과 맞습니다. 이제 문자열 'abcbd'
에 이 RE를 맞추는 것을 그려봅시다.
단계 | 맞춰진 것 | 설명 |
1 | a | RE 안의 a 와 맞음. |
2 | abcbd | 엔진이 문자열의 끝인 갈 수 있는 데까지 간 [bcd]* 를 맞춤. |
3 | 실패 | 엔진이 b 맞추기를 시도하지만, 현재 위치가 문자열의 끝, 그래서 실패 |
4 | abcb | 돌아옴, 그래서 [bcd]* 가 한 글자 적게 맞춰짐. |
5 | 실패 | b 를 다시 시도, 그러나 현재 위치가 마지막 문자인 'd' . |
6 | abc | 다시 돌아옴, [bcd]* 는 bc 만 맞춰짐. |
6 | abcb | b 를 다시 시도. 이번엔 현재 위치의 문자가 'b' , 그래서 성공. |
이제 RE의 끝에 닿았고, 'abcb'
와 맞춰졌습니다. 이것은 매칭 엔진이 어떻게 먼저 갈 수 있는 곳까지 가고, 안 맞으면 계속 돌아오면서 RE의 나머지와 계속해서 재시도하는지를 보여줍니다. 이건 [bcd]*
가 한번도 안맞을 때까지 돌아오고, 계속 실패했다면, 엔진은 그 문자열이 그 RE와 하나도 안맞는다고 결론내렸을 겁니다.
다른 반복 메타문자는 한번 또는 그 이상과 맞추는 +
입니다. *
와 +
사이의 차이에 주의하세요; *
는 0 또는 그 이상과 맞추므로 반복될 것이 아예 없을 수도 있는 반면, +
는 적어도 한번은 있어야 합니다. 비슷한 예를 들면, ca+t
는 'cat'
(1 'a'
), 'caaat'
(3 'a'
), 와 맞지만, 'ct'
와는 맞지 않습니다.
두 개의 반복 수식자가 더 있습니다. 물음표 ?
는 1번 아니면 0번과 맞습니다; 여러분은 어떤 것이 옵션으로 있다고 생각할 수 있습니다. 예를 들어, home-?brew
는 'homebrew'
, 아니면 'home-brew'
중 하나와 맞습니다.
가장 복잡한 반복 수식자는 {m,n}
입니다. 여기서 m과 n은 10진수입니다. 이 수식자는 거기에 최소 m에서 최대 n의 반복이 있어야 함을 뜻합니다. 예를 들어, a/{1,3}b
는 'a/b'
, 'a//b'
, 그리고 'a///b'
와 맞을 겁니다. 그러나 슬래시가 없는 'ab'
, 또는 4개를 가지는 'a////b'
와는 맞지 않습니다.
여러분은 m이나 n 중 하나를 생략할 수 있습니다. 이런 경우, 없는 값은 타당한 값으로 가정됩니다. m을 생략하면 하한이 0으로 처리되는 반면, n을 생략하면 상한이 무한이 됩니다.
환원주의자 성향의 독자들은 다른 세가지 수식자가 모두 이 표기를 이용해서 표현될 수 있음을 알아차렸을 겁니다. {0,}
는 *
와 같고, {1,}
는 +
와 동등하고, {0,1}
은 ?
와 같습니다. 짧고 읽기 쉬우니까 그냥 사용할 때 *
, +
, 나 ?
를 사용하는게 더 좋습니다.
정규식 사용하기
간단한 정규식을 보고난 지금, 우리는 어떻게 이걸 파이썬에서 사용할 수 있을까요? re 모듈이 여러분이 RE를 객체로 컴파일하고, 그걸로 맞추도록 할 수 있는 정규식 엔진으로의 인터페이스를 제공합니다.
정규식 컴파일하기
정규식은 맞춰지는 패턴을 찾는다거나 문자열 치환을 하는 것 같은 다양한 작업을 위한 메소드를 가지는 패턴 객체로 컴파일됩니다.
>>> import re >>> p = re.compile('ab*') >>> p re.compile('ab*')
re.compile() 또한 다양한 특별 기능과 문법 변형을 켜는데 사용되는 옵션으로 flags 인자를 받아들입니다. 우리는 나중에 이용가능한 셋팅으로 넘어갈 겁니다만, 지금은 간단한 예제만 해볼 겁니다:
>>> p = re.compile('ab*', re.IGNORECASE)
RE는 re.compile() 로 하나의 문자열로서 넘어갑니다. RE들은 문자열로서 처리되는데 왜냐하면 정규식은 코어 파이썬 언어의 일부가 아니고, 특별한 문법이 그들을 표현하기 위해서 만들어지지 않았기 때문입니다. (RE가 전혀 필요없는 애플리케이션들이 있으니까 그들을 포함함으로써 언어 사양을 부풀릴 필요는 없습니다.) 대신, re 모듈은 간단히 그냥 socket이나 zlib 모듈처럼 파이썬에 포함된 하나의 C 확장 모듈입니다.
문자열로 된 RE는 파이썬 언어를 더 간결하게 유지하게 해주지만, 다음 절의 주제인 단점도 하나 가지고 있습니다.
백슬래시 괴롭힘
앞서 언급한대로, 정규식은 특별한 형식을 나타내거나 특별 문자를 특별한 뜻의 적용 없이 사용되도록 하기 위해서 백슬래시 문자(''
)를 사용합니다. 이는 문자열 리터럴 안에서 같은 목적을 위해서 같은 문자를 사용하는 파이썬의 사용법과 충돌합니다.
여러분이 LaTeX 파일에 있을 수 있는 문자열 section
과 맞는 RE를 작성하길 원한다고 해봅시다. 프로그램 코드 안에 무엇을 작성해야 할지 그려보기 위해서, 맞추길 원하는 문자열로 시작하세요. 다음, 여러분은 모든 백슬래시나 다른 메타문자들의 앞에 백슬래시를 하나 붙여서 문자열 \section
이 되도록 예외 처리를 해야 합니다. re.compile() 로 넘겨져야 하는 결과 문자열은 \section
이 될 겁니다. 그러나 이를 파이썬 문자열 리터럴로 표현하기 위해서 백슬래시 둘 모두를 다시 예외 처리해야 합니다.
문자들 | 단계 |
section | 맞춰질 텍스트 문자열 |
\section | re.compile()을 위해 예외 처리된 백슬래시 |
"\\section" | 문자열 리터럴을 위해 예외 처리된 백슬래시들 |
요컨대, 리터럴 백슬래시를 맞추기 위해서, RE 문자열로 '\\'
를 작성해야 합니다. 왜냐하면 정규식은 \
가 될테고, 각 백슬래시는 정규 파이썬 문자열 리터럴의 안에서는 \
로 표현되어야 하니까요. 백슬래시들이 반복되는 특성의 RE 안에서, 이것은 많이 반복되는 백슬래시를 이끌고, 결과 문자열을 이해하기 어렵게 만듭니다.
해결 방법은 정규식을 위해 파이썬의 무가공(raw) 문자열 표기를 사용하는 겁니다; 백슬래시들은 'r'
이 앞에 붙은 문자열 리터럴 안에서는 어떤 특별한 방법으로도 처리되지 않습니다. 그래서 r"n"
은 ''
와 'n'
을 포함하는 두 문자의 문자열인 반면, "n"
은 개행문자를 포함하는 한 문자의 문자열입니다. 정규식은 이 무가공 문자열 표기를 사용해서 파이썬 코드로 작성되곤 합니다.
게다가 정규식에서 유효하지만, 파이썬 문자열 리터럴로는 유효하지 않은 특별한 예외 처리 연속들은 이제 무가공 문자열 표기나 백슬래시들의 예외 처리가 되지 않으면 그 연속들이 유효하지 않음을 뜻하는 DeprecationWarning를 만들고 결국 SyntaxError가 될 겁니다.
정규 문자열 | 무가공 문자열 |
"ab*" | r"ab*" |
"\\section" | r"\section" |
"\w+\s+\1" | r"w+s+1" |
맞추기 하기
여러분이 컴파일된 정규식을 나타내는 객체를 갖고 나면, 이걸로 무엇을 할까요? 패턴 객체는 여러 메소드와 속성을 갖습니다. 가장 두드러지는 것만 여기서 다룰 겁니다; 완전한 목록은 re 문서를 살펴보세요.
메소드/속성 | 목적 |
match() | RE가 문자열의 시작에서 맞는지 결정 |
search() | RE가 맞는 위치를 찾기 위해 문자열을 스캔 |
findall() | RE가 맞는 모든 부분 문자열을 찾아서 리스트로 반환 |
finditer() | RE가 맞는 모든 부분 문자열을 찾아서 iterator로 반환 |
맞는 걸 찾을 수 없으면, match()와 search()는 None
을 반환합니다. 그들이 성공하면, 맞춘 것에 대한 정보를 포함하는 match object 인스턴스가 반환됩니다: 맞는 부분 문자열이 어디서 시작하고 끝나는지 등.
여러분은 이에 대해 re모듈과 상호 작용하게 실험함으로써 배울 수 있습니다. 여러분이 tkinter가 사용가능하다면, 여러분은 또한 파이썬 배포판에 포함된 시연 프로그램인 Tools/demo/redemo.py를 보길 원할 수도 있습니다. 이것은 여러분을 RE와 문자열로 들어갈 수 있도록 하고, 어떤 RE가 맞고 틀리는지 보여줍니다. redemo.py
는 복잡한 RE를 디버그하려고 할 때, 정말 유용할 수 있습니다.
이 사용법은 그 예제를 위해 표준 파이썬 인터프리터를 사용합니다. 먼저 파이썬 인터프리터를 실행하고, re모듈을 import하고, RE를 컴파일하세요:
>>> import re >>> p = re.compile('[a-z]+') >>> p re.compile('[a-z]+')
이제 여러분은 RE [a-z]+
에 맞는 다양한 문자열 맞춤을 시도할 수 있습니다. 빈 문자열은 +가 ‘하나 또는 그 이상의 반복’을 뜻하기 때문에 전부 맞지 않습니다. match() 는 이 경우 인터프리터가 아무 출력도 찍지 않게 하는 None
을 반환합니다. 여러분은 이를 명확히 하기 위해서 명시적으로 match()의 결과를 찍을 수 있습니다.
>>> p.match("") >>> print(p.match("")) None
이제 tempo
같은 맞아야 하는 문자열로 시도해 봅시다. 이 경우 match() 는 match object를 반환하므로, 여러분은 이후의 사용을 위해 변수 안에 그 결과를 저장하는 것이 좋습니다.
>>> m = p.match('tempo') >>> m <re.Match object; span=(0, 5), match='tempo'>
이제 여러분은 맞는 문자열에 관한 정보를 match object에 질의할 수 있습니다. Match object 인스턴스는 또한 여러 메소드와 속성을 갖습니다; 가장 중요한 것들은 다음과 같습니다:
메소드/속성 | 목적 |
group() | RE에 맞는 문자열을 반환 |
start() | 맞는 시작 위치를 반환 |
end() | 맞는 끝 위치를 반환 |
span() | 맞는 (시작, 끝) 위치를 포함하는 튜플을 반환 |
이 메소드들을 해보는게 그들의 뜻을 금방 명확하게 만들 겁니다:
>>> m.group() 'tempo' >>> m.start(), m.end() (0, 5) >>> m.span() (0, 5)
group() 은 RE와 맞는 부분 문자열을 반환한다. start() 와 end() 는 맞춘 것에 시작과 끝 인덱스를 반환합니다. span() 은 하나의 튜플로 시작과 끝 인덱스를 모두 반환합니다. match()가 그 RE가 문자열의 시작과 맞는지만 확인하기 때문에, start()는 항상 0이 될 겁니다. 그러나, 패턴의 search() 메소드는 문자열을 스캔하므로, 맞는 것이 이 경우 0에서 시작하지 않을 수 있습니다.
>>> print(p.match('::: message')) None >>> m = p.search('::: message'); print(m) <re.Match object; span=(4, 11), match='message'> >>> m.group() 'message' >>> m.span() (4, 11)
실제 프로그램에서 가장 일반적인 스타일은 match object를 한 변수 안에 저장하고, 그러고 나서 그게 None
인지를 확인하는 겁니다. 이렇게 보입니다:
p = re.compile( ... ) m = p.match( 'string goes here' ) if m: print('Match found: ', m.group()) else: print('No match')
두 패턴 메소드들은 패턴을 위한 모든 맞춘 것들을 반환합니다. findall() 은 맞는 문자열의 리스트를 반환합니다:
>>> p = re.compile(r'd+') >>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping') ['12', '11', '10']
리터럴을 무가공 문자열 리터럴로 만드는 r
접두어는 이 예제에서 필요합니다. 왜냐하면 파이썬에서 인식되지 않는, 정규식에서는 반대인, 보통의 “cooked” 문자열 리터럴 내의 예외 처리 시퀀스는 이제 DeprecationWarning을 만들고, 결국 SyntaxError가 될 겁니다. 백슬래시 괴롭힘을 보세요.
findall()은 그 결과를 반환하기 전에 전체 리스트를 생성해야 합니다. finditer() 메소드는 iterator로 match object의 시퀀스를 리턴합니다:
>>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...') >>> iterator <callable_iterator object at 0x...> >>> for match in iterator: ... print(match.span()) ... (0, 2) (22, 24) (29, 31)
모듈-레벨 함수
여러분은 패턴 객체를 만들고 그 메소드들을 호출하진 않아도 됩니다: re 모듈 또한 match(), search(), findall(), sub() 등등으로 불리는 탑 레벨 함수를 제공합니다. 이들 함수들은 해당하는 패턴 메소드와 첫번째 인자로 추가된 RE 문자열과 함께 같은 인자를 갖고, 여전히 None
이나 match object 인스턴스 중 하나를 반환합니다.
>>> print(re.match(r'Froms+', 'Fromage amk')) None >>> re.match(r'Froms+', 'From amk Thu May 14 19:12:10 1998') <re.Match object; span=(0, 5), match='From '>
내부적으로 이들 함수는 간단히 여러분을 위한 패턴 객체를 생성하고, 그에 대한 적절한 메소드를 호출합니다. 그들은 또한 캐시 안에 컴파일된 객체를 저장하고, 그래서 같은 RE를 사용하는 이후의 호출들은 그 패턴을 다시 계속해서 파싱할 필요가 없을 겁니다.
이들 모듈 레벨 함수들을 사용해야 할까요? 아니면 패턴을 얻고 여러분 스스로 이들 메소드를 호출해야 할까요? 여러분이 루프 안에서 regex를 접근하고 있다면, 이를 미리 컴파일하는 것이 약간의 함수 호출을 절약해줄 겁니다. 루프의 밖에서는 내부 캐시 덕분에 큰 차이는 없을 겁니다.
컴파일 기호
컴파일 기호로 정규식이 어떻게 동작하는지의 측면을 바꿔봅시다. 기호는 하나는 IGNORECASE
와 같이 긴 이름과 I
같은 한글자의 짧은 이름, 이 두가지 이름으로 re 모듈 안에서 이용 가능합니다. (여러분이 Perl의 패턴 수식자에 친숙하다면, 한글자 형식은 같은 문자를 사용합니다; 예를 들어, re.VERBOSE의 짧은 형식은 re.X) 비트 OR 처리함으로써 여러 기호를 함께 사용할 수도 있습니다; 예를 들어, re.I | re.M
는 I
과 M
기호 모두 셋팅합니다.
여기 이용 가능한 기호의 표입니다. 각각의 더 자세한 설명이 뒤에 따릅니다.
기호 | 의미 |
ASCII , A | w , b , s 그리고 d 와 같은 여러 예외 처리를 각각의 속성으로 ASCII 문자 상에만 맞춤 |
DOTALL , S | . 을 개행문자를 포함하는 아무 문자와 맞춤 |
IGNORECASE , I | 대소문자 구별을 하지 않고 맞춤 |
LOCALE , L | 지역 설정에 맞게 맞춤 |
MULTILINE , M | ^ 와 $ 에 영향을 주는 여러 줄 맞춤 |
VERBOSE , X (‘확장’을 위함) | 더 명확히, 더 이해하기 쉽게 조직할 수 있는, verbose REs를 켬. |
I
IGNORECASE
대소문자 구별 없는 맞추기를 수행함; 문자 클래스와 리터럴 문자열들은 대소문자 구별 없이 문자를 맞출 것입니다. 예를 들어, [A-Z]
는 소문자 역시 맞출 것입니다. ASCII
기호가 아스키 문자가 아닌 맞추기를 끄기 위해 사용되기 전까지는 완전한 유니코드 맞추기가 수행될 겁니다. [a-z]
나 [A-Z]
가 IGNORECASE
기호의 조합과 함께 사용되면, 52개의 아스키 문자와 4개의 추가 아스키가 아닌 문자를 맞출 겁니다: ‘İ’ (U+0130, 위에 점이 있는 라틴 문자 I), ‘ı’ (U+0131, 점이 없는 라틴 소문자 i), ‘ſ’ (U+017F, 라틴 소문자 긴 s), 그리고 ‘K’ (U+212A, 켈빈(Kelvin) 기호). Spam
은 'Spam'
, 'spam'
, 'spAM'
, 또는 'ſpam'
(유니코드 모드에서만 글자가 맞음)이 맞을 겁니다. 이 소문자 만들기는 현재 지역 설정을 고려하지 않습니다; LOCALE
기호를 설정하면 될 겁니다.
L
LOCALE
유니코드 데이터베이스 대신 현재 지역 설정에 따르도록 w
, W
, b
, B
와 대소문자 구별 없는 맞추기를 만듦
지역 설정은 언어 차이를 고려한 프로그램 작성에 도움을 주기 위한 C 라이브러리의 기능입니다. 예를 들어, 여러분이 인코딩된 프랑스 글을 처리할 때, 여러분은 단어를 맞추기 위해서 w+
를 쓸 수 있도록 하고 싶을 겁니다만, w
는 바이트 패턴으로 문자 클래스 [A-Za-z]
만 맞출 겁니다; é
나 ç
에 해당하는 바이트를 맞추지 않을 겁니다. 여러분의 시스템이 적절히 설정되어 있고, 프랑스 지역 설정이 골라져있다면, 그런 C 함수들은 그 프로그램에게 é
에 해당하는 바이트도 글자로 고려되어야 한다고 알려줄 겁니다. 정규식을 컴파일할 때 LOCALE
기호를 설정하는 것은 w
를 위한 이 C 함수들을 사용하도록 컴파일된 객체의 결과를 만들 겁니다; 이건 더 느립니다만 여러분이 기대하는대로 w+
가 프랑스 단어를 맞추도록 해줍니다. 이 기호의 사용은 파이썬 3에서는 지역 설정 메카니즘이 매우 신뢰할 수 없어서 막히고, 시간에서 “culture” 하나만 처리하고, 8비트 지역설정으로만 동작합니다. 유니코드 (str) 패턴을 위한 유니코드 맞추기는 파이썬 3에서는 이미 기본으로 켜져 있고, 다른 지역 설정/언어를 처리할 수 있습니다.
M
MULTILINE
(^
과 $
는 아직 설정되지 않았습니다; 그들은 다른 메타문자들 절에서 소개될 겁니다.)
보통 ^
는 문자열으 시작에서만 맞춰지고, $
는 문자열의 끝과 (만약 있다면,) 문자열의 끝에서의 개행 문자 직전이 맞춰집니다. 이 기호가 지정되면, ^
는 문자열의 시작과 그 문자열 안에서 각 개행 문자가 바로 붙는 각 줄의 시작에서 맞춰집니다. 비슷하게, $
메타문자는 문자열의 끝 아니면 각 개행 문자의 직전인 각 줄의 끝과 맞춰집니다.
S
DOTALL
특수 문자 '.'
을 개행 문자를 포함하는 모든 문자와 맞추도록 만듭니다; 이 기호 없이는 '.'
는 개행 문자를 제외한 나머지와 맞춰집니다.
A
ASCII
w
, W
, b
, B
, s
와 S
를 완전한 유니코드 맞추기 대신 아스키만 맞춥니다. 이는 유니코드 패턴에서만 의미가 있고, 바이트 패턴에서는 무시됩니다.
X
VERBOSE
이 기호는 여러분을 여러분이 그들을 구성하는 방법을 더 유연하게 해줌으로써 더 읽기 쉬운 정규식을 작성하도록 해줍니다. 이 기호가 지정되면, RE 문자열 내의 공백 기호가 문자 클래스 안이나 예외 처리되지 않은 백슬래시 앞에 붙을 때를 제외하고는 무시됩니다; 이는 RE를 더 명확히 구성하고 들여쓸 수 있게 합니다. 이 기호는 또한 여러분이 RE 내에 엔진에 의해서는 무시될 주석을 쓸 수 있게 합니다; 주석은 문자 클래스 안이나 예외 처리되지 않은 백슬래시 앞이 아닌 곳에서 '#'
에 의해 표기됩니다.
예를 들면, 여기 re.VERBOSE
를 사용하는 RE가 있습니다; 보세요. 얼마나 읽기 쉬워졌습니까?
charref = re.compile(r""" &[#] # Start of a numeric entity reference ( 0[0-7]+ # Octal form | [0-9]+ # Decimal form | x[0-9a-fA-F]+ # Hexadecimal form ) ; # Trailing semicolon """, re.VERBOSE)
verbose 셋팅 없이는, RE는 다음처럼 보이게 됩니다:
charref = re.compile("&#(0[0-7]+" "|[0-9]+" "|x[0-9a-fA-F]+);")
위의 예제에서 파이썬의 문자열 리터럴의 자동 이어붙임이 RE를 더 작은 조각으로 나누기 위해서 사용되었습니다만, 여전히 re.VERBOSE
를 사용한 버전보다 읽기 어렵습니다.
더 큰 패턴 파워
지금까지 우리는 정규식의 기능 중 일부만 다뤘습니다. 이 절에서 우리는 새로운 메타 문자과 어떻게 맞춰진 글의 일부를 얻어오기 위해 그룹을 사용하는지를 다룰 겁니다.
더 많은 메타 문자
우리가 아직 다루지 않은 여러 메타 문자들이 있습니다. 대부분을 이 절에서 다룰 겁니다.
이야기할 남은 메타 문자들 중 일부는 0-너비 단정문(zero-width assertions)입니다. 그들은 엔진이 문자열을 미리 가보도록 하지 않습니다; 대신 전혀 문자를 소비하지 않고, 간단히 성공하든지 실패합니다. 예를 들어, b
는 현재 위치가 단어 경계에 위치하지 않음을 단정합니다; 그 위치는 전혀 b
에 의해 바뀌지 않습니다. 이것은 0-너비 단정이 절대 반복되면 안됨을 의미합니다. 왜냐하면, 그들이 주어진 위치에서 한번 맞춰지면, 그들은 분명히 무한번 맞춰질 수 있기 때문입니다.
|
대안, 또는 “or” 연산자. A와 B가 정규식이라면, A|B
는 A 아니면 B와 맞는 어떤 문자와 맞을 겁니다. |
는 여러 문자의 문자열을 대체할 때 합리적으로 그를 동작하도록 만들기 위해서 매우 낮은 우선 순위를 갖습니다. Crow|Servo
는 'Crow'
아니면 'Servo'
와 맞춰지지만, 'Cro'
, 'w'
나 'S'
, 그리고 'ervo'
와는 안맞습니다.
리터럴 '|'
를 맞추기 위해서는 |
를 사용하거나, 문자 클래스 안에 [|]
처럼 넣으세요.
^
줄의 시작과 맞습니다. MULTILINE
기호가 설정되기 전까지, 이것은 문자열의 시작과만 맞을 겁니다. MULTILINE
모드에서 이것은 또한 문자열 내의 각 개행문자 직후와 맞습니다.
예를 들어, 여러분이 줄 시작에 있는 단어 From
만 맞추길 원한다면, 사용될 RE는 ^From
입니다.
>>> print(re.search('^From', 'From Here to Eternity')) <re.Match object; span=(0, 4), match='From'> >>> print(re.search('^From', 'Reciting From Memory')) None
리터럴 '^'
를 맞추려면, ^
를 사용하세요.
$
문자열의 끝 아니면 개행 문자 다음의 어떤 위치로 정의되는 줄의 끝과 맞습니다.
>>> print(re.search('}$', '{block}')) <re.Match object; span=(6, 7), match='}'> >>> print(re.search('}$', '{block} ')) None >>> print(re.search('}$', '{block}n')) <re.Match object; span=(6, 7), match='}'>
리터럴 '$'
를 맞추려면, $
를 사용하거나, 문자 클래스 안에 [$]
처럼 넣으세요.
A
문자열의 시작과만 맞습니다. MULTILINE
모드가 아니면, A
와 ^
는 사실상 같습니다. MULTILINE
모드에서 그들은 다릅니다: A
는 여전히 문자열의 시작과만 맞습니다만, ^
는 문자열 내의 개행 문자 다음의 위치에서만 맞을 수 있습니다.
Z
문자열의 끝과만 맞습니다.
b
단어 경계. 단어의 시작이나 끝에서만 맞는 0-너비 단정입니다. 단어는 글자와 숫자의 연속으로 정의되므로, 단어의 끝은 공백 문자나 글자와 숫자가 아닌 문자로 나타내어 집니다.
다음 예는 완전한 단어인 class
와만 맞춰집니다; 다른 단어 내에 포함된 것은 맞춰지지 않습니다.
>>> p = re.compile(r'bclassb') >>> print(p.search('no class at all')) <re.Match object; span=(3, 8), match='class'> >>> print(p.search('the declassified algorithm')) None >>> print(p.search('one subclass is')) None
이 특별한 시퀀스를 사용할 때 기억해야 할 두가지가 있습니다. 첫째, 이것은 파이썬의 문자열 리터럴과 정규식 시퀀스 사이에 가장 나쁜 충돌입니다. 파이썬의 문자열 리터럴 안에서, b
는 백스페이스 문자인, 아스키 값 8입니다. 여러분이 무가공 문자열을 사용하지 않는다면, 파이썬은 b
를 백스페이스로 변환할 것이고, 여러분의 RE는 기대하는 것처럼 맞지 않을 겁니다. 다음 예제는 우리의 이전 RE와 같은 것을 보입니다만, RE 문자열의 앞에 'r'
을 생략합니다.
>>> p = re.compile('bclassb') >>> print(p.search('no class at all')) None >>> print(p.search('b' + 'class' + 'b')) <re.Match object; span=(0, 7), match='x08classx08'>
둘째, 이 단정의 사용이 없는 문자 클래스 안에서, b
는 파이썬 문자열 리터럴의 호환성을 위해서 백스페이스 문자를 나타냅니다.
B
b
의 반대인, 현재 위치가 단어 경계가 아닐 때 맞는 다른 0-너비 단정
그룹 짓기
종종 여러분은 그냥 어떤 RE가 맞는지 아닌지보다 더 많은 정보를 얻을 필요가 있습니다. 정규식은 종종 다른 용도의 구성 요소와 맞는 여러 하위 그룹으로 나뉜 RE를 작성함으로써 문자열을 나누는데 사용됩니다. 예를 들면, RFC-822 헤더 줄은 헤더 이름과 값이 ':'
로 분리되어 다음과 같이 나뉩니다:
From: author@example.com User-Agent: Thunderbird 1.5.0.9 (X11/20061227) MIME-Version: 1.0 To: editor@example.com
이는 헤더 이름과 맞는 그룹 하나와 헤더 값과 맞는 다른 그룹 하나를 갖는, 전체 헤더 줄과 맞는 정규식을 작성함으로써 처리할 수 있습니다.
그룹들은 '('
, ')'
메타 문자로 표기됩니다. '('
와 ')'
는 그들이 수학식에서 하는 것과 상당히 같은 뜻을 갖습니다; 그들은 그들 내부에 표현된 식들을 함께 그룹짓고, *
, +
, ?
, 나 {m,n}
같은 반복 한정자로 한 그룹의 내용을 반복할 수 있습니다. 예를 들어, (ab)*
는 ab의 0 또는 그 이상의 반복과 맞을 겁니다.
>>> p = re.compile('(ab)*') >>> print(p.match('ababababab').span()) (0, 10)
'('
, ')'
로 나타내어지는 그룹은 또한 그들이 맞춘 글의 첫번째와 마지막 인덱스를 잡아옵니다; 이것은 group()
, start()
, end()
, 그리고 span()
에 인자로 넘김으로써 얻을 수 있습니다. 그룹은 0으로 시작해서 번호가 붙습니다. 그룹 0은 언제나 존재합니다; 이것은 전체 RE입니다. 그래서 match object 메소드들은 모두 그들의 기본 인자로 그룹 0을 갖습니다. 이후에 우리는 어떻게 맞춘 글에서 잡아오지 않는 범위의 그룹을 표현하는지를 볼 겁니다.
>>> p = re.compile('(a)b') >>> m = p.match('ab') >>> m.group() 'ab' >>> m.group(0) 'ab'
하위 그룹들은 왼쪽에서 오른쪽으로 1부터 번호가 붙습니다. 그룹들은 겹쳐질 수 있습니다; 번호를 결정하기 위해서, 그냥 열린 괄호 문자를 왼쪽에서 오른쪽으로 가면서 세세요.
>>> p = re.compile('(a(b)c)d') >>> m = p.match('abcd') >>> m.group(0) 'abcd' >>> m.group(1) 'abc' >>> m.group(2) 'b'
group()
는 그 그룹을 위한 해당하는 값을 포함하는 튜플을 반환하는 경우에 한번에 여러 그룹 번호를 넘길 수 있습니다.
>>> m.group(2,1,2) ('b', 'abc', 'b')
groups()
메소드는 1부터 있는 만큼의 모든 하위 그룹에 대한 문자열을 담고 있는 튜플을 반환합니다.
>>> m.groups() ('abc', 'b')
패턴에서의 역참조는 여러분이 문자열 내의 현재 위치에서도 찾을 수 있을만한, 앞쪽의 잡아온 그룹의 내용을 지정할 수 있도록 합니다. 예를 들어, 1
는 그룹 1의 정확한 내용을 현재 위치에서 찾을 수 있다면 성공하고, 아니라면 실패합니다. 파이썬의 문자열 리터럴 또한 숫자가 붙는 백슬래시를 문자열 내의 임의의 문자를 포함할 수 있도록 하기 위해서 사용한다는 것을 기억하세요. 그러므로 RE 내의 역참조를 결합할 때 무가공 문자열을 사용해야 함을 명심하세요.
예를 들어, 다음 RE는 문자열 내의 두번 반복되는 단어를 찾습니다.
>>> p = re.compile(r'b(w+)s+1b') >>> p.search('Paris in the the spring').group() 'the the'
이런 역참조는 문자열 내에서 검색을 위해서는 거의 유용하지 않습니다만 — 드물게 이런 방식으로 반복되는 데이터의 글 형식이 있습니다 — 여러분은 곧 문자 치환을 수행할 때 그들이 매우 유용한 것을 발견할 겁니다.
잡아오지 않기와 이름 지은 그룹
정교한 RE는 찾고 싶은 문자열을 잡아 오기 위해서, 또 RE 스스로를 그룹 짓고 구성하도록 하는, 둘 모두를 위해서 많은 그룹을 사용할 수 있습니다. 복잡한 RE에서 그룹 번호의 추적을 유지하는 것은 어려워 집니다. 이 문제를 도와주는 두가지 기능이 있습니다. 둘 모두 정규식 확장을 위한 공통 문법을 사용하기 때문에, 우리는 그걸 먼저 볼 겁니다.
Perl 5는 정규식을 표준화하는 그 강력한 부가기능으로 잘 알려져 있습니다. 이들 새로운 기능을 위해 Perl 개발자들은 표준 RE들로부터 혼란스럽게 다른 Perl의 정규식을 만드는 일 없이 새로운 키 한번의 메타문자나 로 시작하는 새로운 특수 시퀀스를 선택할 수 없었습니다. 예를 들어, 그들이 새로운 메타문자로
&
를 골랐다면, 이전 식들은 &
가 정규 문자였고 &
나 [&]
를 씀으로써 예외 처리되지 않았었음을 가정하게 됩니다.
Perl 개발자들이 선택한 해결책은 확장 문법으로 (?...)
를 사용하는 것이었습니다. ?
는 반복을 갖지 않아서 괄호 바로 뒤의 ?
는 문법 에러였고 그래서 어떤 호환성 문제도 일으키지 않았었습니다. ?
바로 뒤의 문자는 무슨 확장이 사용될 것인지를 나타냅니다. 그래서 (?=foo)
는 어떤 한가지(긍정 예견 단정)이고 (?:foo)
는 다른 한가지(하위식 foo
를 포함하는 잡아오지 않는 그룹)입니다.
파이썬은 Perl의 확장 중 여럿을 지원하고 Perl의 확장 문법에 표현식 문법을 추가합니다. 물음표 뒤에 첫번째 문자가 P
라면 여러분은 파이썬에 지정되는 확장임을 압니다.
우리가 일반 확장 문법을 본 지금, 우리는 복잡한 REs에서 그룹으로 작업하는 것을 간략화하는 기능으로 돌아올 수 있습니다.
어떤 때 여러분은 정규식의 일부를 의미하지만 그룹의 내용을 얻는데는 관심이 없는 그룹을 사용하길 원할 겁니다. 여러분은 잡아오지 않는 그룹을 사용해서 명시적으로 이렇게 만들 수 있습니다: 어떤 다른 정규식으로 여러분이 ...
을 대체하는 (?:...)
>>> m = re.match("([abc])+", "abc") >>> m.groups() ('c',) >>> m = re.match("(?:[abc])+", "abc") >>> m.groups() ()
그 그룹이 맞는 내용을 얻을 수 없는 사실을 제외하면, 잡아오지 않는 그룹은 정확히 잡아오는 그룹과 똑같은 동작을 합니다; 여러분은 그 안에 아무거나 넣을 수 있고, *
같은 반복 메타문자로 반복할 수도, 다른 그룹 (잡아오거나 잡아오지 않거나) 안에 겹칠 수도 있습니다. (?:...)
는 여러분이 모든 다른 그룹의 번호를 붙이는 것을 바꾸는 일 없이 새로운 그룹을 추가할 수 있는 한, 기존의 패턴을 수정할 때 특히 유용합니다. 잡아오는 그리고 잡아오지 않는 그룹 사이의 검색에서 성능 차이가 없다는 점도 언급되는게 좋겠습니다; 다른 것보다 둘다 빠르지 않습니다.
그 이상의 눈에 띄는 기능은 이름 지은 그룹입니다: 번호로 참조되는 대신, 그룹이 이름으로 참조될 수 있습니다.
이름 지은 그룹의 문법은 파이썬 특정 확장 중 하나입니다: (?P<name>...)
. name 은 분명하게 그 그룹의 이름입니다. 이름 지은 그룹은 잡아오는 그룹과 정확히 같은 동작을 하고, 추가로 그 그룹에 이름을 연관짓습니다. 잡아오는 그룹으로 일하는 match object 메소드들 모두 번호로 그 그룹을 참조하는 정수나 원하는 그 그룹의 이름을 담는 문자열 중 하나를 받아들입니다. 이름 지은 그룹들은 여전히 번호가 붙기 때문에 여러분은 두가지 방법으로 한 그룹의 정보를 얻을 수 있습니다:
>>> p = re.compile(r'(?P<word>bw+b)') >>> m = p.search( '(((( Lots of punctuation )))' ) >>> m.group('word') 'Lots' >>> m.group(1) 'Lots'
이름 지 그룹들은 그들이 여러분에게 번호를 기억하는 대신 쉽게 기억되는 이름으로 사용되기 때문에 간편합니다. 여기 imaplib
모듈로부터의 예제 RE입니다:
InternalDate = re.compile(r'INTERNALDATE "' r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-' r'(?P<year>[0-9][0-9][0-9][0-9])' r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])' r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])' r'"')
그룹 9를 얻는 것을 기억하는 대신 m.group('zonem')
를 얻는 것이 명확히 더 쉬쉽니다.
표현식 내의 역참조를 위한 (...)1
같은 문법은 그룹의 번호를 참조합니다. 번호 대신 그룹 이름을 사용하는 변형이 자연스럽게 있습니다. 이는 다른 파이썬 확장입니다: (?P=name)
는 현재 점에서 다시 맞을 name 으로 불리는 그룹의 내용을 나타냅니다. 두번 반복되는 단어를 찾는 정규식, b(w+)s+1b
은 b(?P<word>w+)s+(?P=word)b
로도 쓰일 수 있습니다:
>>> p = re.compile(r'b(?P<word>w+)s+(?P=word)b') >>> p.search('Paris in the the spring').group() 'the the'
예견 단정문
다른 0-너비 단정문은 예견 단정문입니다. 예견 단정문은 긍정과 부정 형식 모두로 이용 가능하고, 이렇게 보입니다:
(?=...)
긍정 예견 단정문. 이것은 여기서 ...
로 표현된 정규식이 현재 위치에서 맞으면 성공하고, 아니면 실패합니다. 그러나 포함된 식이 시도되고 나면 매칭 엔진은 전혀 앞으로 나가지 않습니다; 패턴의 나머지는 단정문이 시작된 바로 거기서 시도됩니다.
(?!...)
부정 예견 단정문. 이것은 긍정 단정의 반대입니다: 포함된 식이 문자열 안의 현재 위치에서 맞지 않으면 성공합니다.
더 확실히 하기 위해서, 예견이 유용한 경우를 보죠. 파일 이름을 맞추고 이를 앞쪽 이름과 확장자로 .
로 나누기 위한 간단한 패턴을 봅시다. 예를 들어, news.rc
에서, news
가 앞쪽 이름이고 rc
가 파일 이름의 확장자입니다.
이를 맞추기 위한 패턴은 매우 간단합니다:
.*[.].*$
.
이 메타문자라서 특별히 다뤄질 필요가 있음을, 그래서 그 지정된 문자만 맞추기 위해서 문자 클래스 안에 있음을 주의하세요. 끝에 붙은 $
도 주의하세요; 이것은 문자열의 나머지 모두가 확장 안에 포함되어야 함을 보장하기 위해 붙습니다. 이 정규식은 foo.bar
와 autoexec.bat
와 sendmail.cf
그리고, printers.conf
와 맞습니다.
이제 약간 복잡한 문제를 생각해보죠; 여러분이 확장자가 bat
가 아닌 파일 이름을 맞추고 싶다면 어떻게 해야 할까요? 이런 틀린 시도는 어떨까요:
.*[.][^b].*$
첫번째 위의 시도는 확장자의 첫번째 문자가 b
가 아닌 것을 요구함으로써 bat
를 제외시려고 했습니다. 틀렸습니다. 왜냐하면 패턴이 foo.bar
도 못맞추거든요.
.*[.]([^b]..|.[^a].|..[^t])$
여러분이 맞추기 위한 다음 경우를 요구하면서 첫번째 해결책의 패치를 시도하면 식이 좀 더 지저분해집니다: 첫문자가 b
가 아니거나; 두번째 문자가 a
가 아니거나; 또는 세번째 문자가 t
가 아니다. 이것은 foo.bar
를 받아들이고 autoexec.bat
를 거부합니다만, 세 문자 확장자가 필요하고, sendmail.cf
같은 두 문자 확장자로 된 파일 이름은 받아들이지 않을 겁니다. 우리는 이를 고치기 위한 노력으로 패턴을 다시 한번 복잡하게 할 겁니다.
.*[.]([^b].?.?|.[^a]?.?|..?[^t]?)$
세번째 시도에서는 sendmail.cf
같은 세 문자보다 짧은 확장자를 맞출 수 있도록 두번째와 세번째 문자를 모두 옵션으로 만들었습니다.
패턴이 이제 정말로 점점 읽기도 이해하기도 어렵게 복잡해지고 있습니다. 더 나쁜 점은 문제가 바뀌어서 여러분이 확장자 bat
와 exe
둘 다 제외하고 싶다면, 그 패턴이 훨씬 더 복잡해지고 헷갈릴겁니다.
부정 예견이 이런 모든 혼란을 차단합니다:
.*[.](?!bat$)[^.]*$
부정 예견은 다음을 의미합니다: 표현식 bat
가 이 지점에서 맞지 않으면, 패턴의 나머지를 시도하세요; bat$
가 맞으면, 전체 패턴은 실패할 겁니다. 끝에 붙는 $
는 확장자가 bat
로만 시작하면서 허용될, sample.batch
같은 것을 보장하기 위해 필요합니다. [^.]*
는 파일 이름에 여러 개의 점이 있을 때 그 패턴이 동작함을 확실히 합니다.
다른 파일 이름 확장자를 제외하는 것은 이제 쉽습니다; 그냥 단정문 내부에 다른 것을 추가하세요. 다음 패턴은 bat
나 exe
로 끝나는 파일 이름을 제외합니다:
.*[.](?!bat$|exe$)[^.]*$
문자열 수정하기
이 지점까지 위에서, 우리는 간단히 고정된 문자열을 찾는 것을 수행해 보았습니다. 정규식은 또한 보통 다양한 방법으로 다음 패턴 메소드를 사용해서 문자열을 수정하는데 사용됩니다.
메소드/속성 | 목적 |
split() | RE가 맞을 때마다 나눠서 문자열을 리스트로 나눕니다. |
sub() | RE가 맞는 곳에서 모든 부분 문자열을 찾아서, 다른 문자열로 그들을 바꿉니다. |
subn() | sub()와 같은 것을 합니다만, 새로운 문자열과 바꾼 횟수를 반환합니다. |
문자열 나누기
패턴의 split()
메소드는 RE가 맞을 때마다 문자열을 나누고 조각들의 리스트를 반환합니다. 문자열의 split()
메소드와 비슷하지만, 여러분이 나누기 위해 사용하는 구분자에서 더 많은 일반성을 제공합니다; 문자열 split()
은 공백 문자나 고정된 문자열로만 나누는 것을 지원합니다. 여러분이 기대하는 대로 모듈 레벨 re.split()
함수 역시 있습니다.
.
split
(string[, maxsplit=0])
정규식의 맞춤으로 string을 나눔. 잡아오는 괄호가 RE에서 사용되면, 그들의 내용을 반환 리스트의 일부로 반환. maxsplit이 0이 아니면, 최대 maxsplit번 나눔이 이뤄짐.
여러분은 maxsplit의 값을 넘김으로써 나누는 횟수를 제한할 수 있습니다. maxsplit이 0이 아니면, 최대 maxsplit 번의 나눔이 일어나고, 문자열의 나머지는 리스트의 마지막 항목으로 반환됩니다. 다음 예제에서, 구분자는 글자나 숫자가 아닌 문자의 아무 연속이 됩니다.
>>> p = re.compile(r'W+') >>> p.split('This is a test, short and sweet, of split().') ['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', ''] >>> p.split('This is a test, short and sweet, of split().', 3) ['This', 'is', 'a', 'test, short and sweet, of split().']
어떤 때 여러분은 구분자들 사이의 텍스트 뿐만 아니라 구분자가 무엇이었는지는 알 필요가 있습니다. 잡아오는 괄호들이 RE내에서 사용되면, 그들의 값들은 리스트의 일부로 반환됩니다. 다음 호출들을 비교해보세요:
>>> p = re.compile(r'W+') >>> p2 = re.compile(r'(W+)') >>> p.split('This... is a test.') ['This', 'is', 'a', 'test', ''] >>> p2.split('This... is a test.') ['This', '... ', 'is', ' ', 'a', ' ', 'test', '.', '']
모듈 레벨 함수 re.split()
는 첫 인자로 사용되는 RE가 추가됩니다만, 다른 것은 같습니다.
>>> re.split(r'[W]+', 'Words, words, words.') ['Words', 'words', 'words', ''] >>> re.split(r'([W]+)', 'Words, words, words.') ['Words', ', ', 'words', ', ', 'words', '.', ''] >>> re.split(r'[W]+', 'Words, words, words.', 1) ['Words', 'words, words.']
찾기와 바꿈
다른 보통의 작업은 패턴이 모두 맞는 곳을 찾아 다른 문자열로 그들을 바꾸는 겁니다. sub()
메소드는 문자열이나 함수가 될 수 있는, 바꾸는 값과 처리될 문자열을 취합니다.
.
sub
(replacement, string[, count=0])
string내의 RE가 왼쪽부터 겹쳐지지 않는 곳에 있는 것을 replacement로 바꿈으로써 얻은 문자열을 반환. 패턴이 없다면, 바뀌지 않고 string이 반환됨.
옵션 인자 count는 패턴이 맞는 곳에서 바꿀 최대 횟수; count는 음수가 아닌 정수여야 함. 기본 값 0은 모든 곳을 바꿈을 의미.
여기 sub()
메소드 사용의 간단한 예제가 있습니다. 이것은 색의 이름을 단어 colour
로 바꿉니다:
>>> p = re.compile('(blue|white|red)') >>> p.sub('colour', 'blue socks and red shoes') 'colour socks and colour shoes' >>> p.sub('colour', 'blue socks and red shoes', count=1) 'colour socks and red shoes'
subn()
메소드는 같은 동작을 합니다만, 새로운 문자열 값과 수행으로 바뀐 횟수를 가진 2-튜플을 반환합니다:
>>> p = re.compile('(blue|white|red)') >>> p.subn('colour', 'blue socks and red shoes') ('colour socks and colour shoes', 2) >>> p.subn('colour', 'no colours at all') ('no colours at all', 0)
빈 맞춰짐은 그들이 이전 맞춰진 것과 붙어 있지 않을 때만 바뀝니다.
>>> p = re.compile('x*') >>> p.sub('-', 'abxd') '-a-b--d-'
replacement가 문자열이면, 그 안의 어떤 백슬래시 예외도 처리됩니다. 즉, n
는 하나의 개행 문자로 바뀌고, r
는 캐리지 리턴으로 바뀌고, 그렇게 됩니다. &
같은 알려지지 않은 예외처리는 홀로 남습니다. 6
같은 역참조는 RE 내에 해당하는 그룹에 맞는 부분 문자열로 바뀝니다. 이는 원래 글의 일부가 결과가 되는 바뀐 문자열 안으로 포함되도록 합니다.
이 예제는 {
, }
로 둘러싸인 문자열이 뒤에 붙는 단어 section
을 맞추고, section
을 subsection
로 바꿉니다:
>>> p = re.compile('section{ ( [^}]* ) }', re.VERBOSE) >>> p.sub(r'subsection{1}','section{First} section{second}') 'subsection{First} subsection{second}'
(?P<name>...)
문법으로 정의되는 이름 지은 그룹으로 참조되는 문법 또한 존재합니다. g<name>
는 name
으로 이름 지은 그룹에 의해 맞춰진 부분 문자열을 사용하고, g<number>
는 해당하는 그룹 number를 사용합니다. g<2>
는 2
와 동등하지만, g<2>0
같은 바꾸는 문자열 내에서 모호합니다(20
는 리터럴 문자 '0'
가 뒤에 붙는 그룹 2의 참조가 아니라, 그룹 20의 참조로 처리될 겁니다). 다음 치환들은 모두 동등합니다만, 바꾸는 문자열의 세가지 변형을 사용합니다.
>>> p = re.compile('section{ (?P<name> [^}]* ) }', re.VERBOSE) >>> p.sub(r'subsection{1}','section{First}') 'subsection{First}' >>> p.sub(r'subsection{g<1>}','section{First}') 'subsection{First}' >>> p.sub(r'subsection{g<name>}','section{First}') 'subsection{First}'
replacement는 여러분에게 더 많은 제어권을 주는 함수도 될 수 있습니다. replacement가 함수라면, 그 함수는 pattern의 모든 겹치지 않는 맞는 곳마다 불리게 됩니다. 각 호출마다, 그 함수는 맞춰진 것에 대한 match object 인자를 넘기고, 이 정보를 원하는 바꾸는 문자열을 계산하는데 사용하고 반환하는데 사용하고, 이를 반환합니다.
다음 예제에서, 바꾸는 함수는 10진수를 16진수로 변환합니다:
>>> def hexrepl(match): ... "Return the hex string for a decimal number" ... value = int(match.group()) ... return hex(value) ... >>> p = re.compile(r'd+') >>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.') 'Call 0xffd2 for printing, 0xc000 for user code.'
모듈-레벨 함수 re.sub()
함수를 사용할 때, 그 패턴은 첫번째 인자로 넘겨집니다. 그 패턴은 객체 또는 문자열로서 제공될 수 있습니다; 여러분이 정규식 기호를 지정할 필요가 있다면, 여러분은 첫 파라미터로 패턴 객체를 사용하던가, 패턴 문자열로 내장된 수정자를 사용해야 합니다. 예를 들면, sub("(?i)b+", "x","bbbb BBBB")
는 'x x'
를 반환합니다.
공통적인 문제들
정규식은 어떤 용도에서 강력한 도구입니다만, 어떤 방법에서 그 동작은 직관적이지 않고, 여러분이 기대하는 방법으로 동작하지 않는 때도 있습니다. 이 절에서는 대부분의 공통적인 위험 중 일부를 지적할 겁니다.
문자열 메소드를 사용하세요
어떤 때는 re
모듈의 사용이 실수입니다. 여러분이 고정된 문자열이나 한 문자 클래스를 맞추고 있고, 여러분이 IGNORECASE
기호 같은 re
기능을 사용하고 있지 않다면, 정규식의 완전한 능력이 필요하지 않을 수 있습니다. 문자열은 고정된 문자열로 수행하는 동작을 위한 여러 메소드를 가지고, 그들은 보통 훨씬 더 빠릅니다. 왜냐하면 그 구현이 크고 더 일반적인 정규식 엔진 대신, 그 목적을 위해 최적화된 하나의 작은 C 루프이기 때문입니다.
하나의 고정 문자열을 다른 하나로 바꾸는 것이 한 예제가 될 수 있습니다; 예를 들어, 여러분은 word
를 deed
로 바꿀 수 있습니다. re.sub()
는 이를 위해 사용할 함수로 보입니다만, replace()
메소드를 고려할 수 있습니다. replace()
는 단어들 안의 word
또한 swordfish
를 sdeedfish
로 바꿈을, 대충의 RE word
도 똑같이 함을 주의하세요. (단어의 일부 상의 치환을 피하기 위해서, 그 패턴은 word
가 한 쪽에서 단어 경계를 갖기 위해서 bwordb
가 되었어야 할 겁니다. 이는 replace()
의 능력 너머의 일이 됩니다.)
다른 공통적인 작업은 문자열의 한 문자가 나타날 때마다 지우는 것이나 다른 한 문자로 그를 바꾸는 것입니다. 여러분인 이를 re.sub('n', ' ', S)
같은 것으로 할 수 있지만, translate()
이 두 작업 모두를 수행할 수 있고, 어떠한 할 수 있는 정규식 동작보다 빠를 겁니다.
요약하면, re
모듈로 바꾸기 전에, 여러분의 문자를 더 빠르고 간단한 문자열 메소드로 풀 수 있는지를 고려하세요.
match() VS search()¶
search()
가 맞추기 위해 문자열을 앞으로 훑어 나가는 반면, match()
함수는 문자열의 시작에서 RE 가 맞는지만 체크합니다. 이 차이를 마음 속에 담는 것은 중요합니다. 기억하세요. match()는 0에서 시작할 잘 맞는 것만 알릴 겁니다; 0에서 맞지 않으면 match()
는 이를 알리지 않을 겁니다.
>>> print(re.match('super', 'superstition').span()) (0, 5) >>> print(re.match('super', 'insuperable')) None
한편, search()
는 문자열을 쭉 앞으로 훑고, 찾은 첫번째 맞춰진 것을 알릴겁니다.
>>> print(re.search('super', 'superstition').span()) (0, 5) >>> print(re.search('super', 'insuperable').span()) (2, 7)
어떤 때 여러분은 re.match()
를 여러분의 RE 앞에 .*
를 그냥 추가해서 사용하길 유지하고 싶어질 겁니다. 이 유혹을 견디고 대신 re.search()
를 사용하세요. 정규식 컴파일러는 맞는 것을 찾기 위한 프로세스의 속도를 높이기 위해서 REs의 분석을 합니다. 이런 분석은 맞는 것의 첫 문자가 무엇이 되어야 할 지를 찾습니다; 예를 들어, Crow
로 시작하는 패턴은 'C'
로 시작해야만 합니다. 이 분석은 엔진이 첫 문자를 찾는 문자열을 빨리 훑도록, 'C'
를 찾았을 때만 전체 맞추는 것을 시도하도록 합니다.
문자열의 끝을 훑도록 하는, .*
를 추가하는 것은 이 최적화를 막고, RE의 나머지에 맞는 것을 찾도록 백트래킹 합니다. 대신 re.search()
를 사용하세요.
그리디 VS 논-그리디
a*
처럼 정규식을 반복할 때, 결과 동작은 가능한한 많은 패턴을 소비하는 것입니다. 이 사실은 여러분이 중괄호로 둘러싸인 HTML 태그 같은 앞뒤가 맞는 구분자 짝을 맞추려고 할 때 종종 여러분을 물어 뜯습니다. 한 HTML 태그를 맞추려는 대충의 패턴은 .*
의 그리디한 세계로 인해 동작하지 않습니다.
>>> s = '<html><head><title>Title</title>' >>> len(s) 32 >>> print(re.match('<.*>', s).span()) (0, 32) >>> print(re.match('<.*>', s).group()) <html><head><title>Title</title>
그 RE는 '<html>'
안의 '<'
를 맞추고, .*
는 문자열의 나머지를 소비합니다. RE 내에 여전히 더 남아 있긴 하지만, >
는 문자열의 끝에서 맞지 않을 수 있습니다. 그래서 정규식 엔진은 >
를 찾을 때까지 문자마다 백트랙해야 합니다. 마지막 맞추는 것은 '<html>'
안의 '<'
로부터, 여러분이 원치 않는 '</title>'
안의 '>'
까지 확장합니다.
이 경우, 해결책은 가능한 더 작은 글을 맞추는 논-그리디 한정자 *?
, +?
,??
, 또는 {m,n}?
를 사용하는 겁니다. 위의 예제에서, '>'
는 '<'
가 맞은 후 바로 시도되고, 실패하면, 엔진은 한번에 한 문자를 나가고, 각 단계마다 '>'
를 재시도 합니다. 이것은 그냥 바로 결과를 생성합니다:
>>> print(re.match('<.*?>', s).group()) <html>
(HTML이나 XML을 정규식으로 파싱하는 것은 고통스럽다는 것을 주의하세요. 퀵-앤-더티 패턴들이 보통의 경우를 처리하겠지만, HTML과 XML은 분명한 정규식을 깰 특별한 경우를 가집니다; 여러분이 가능한 모든 경우를 처리하는 정규식을 쓸 수 있긴 하지만, 그 패턴은 매우 복잡할 겁니다. HTML 또는 XML 파서 모듈을 이런 작업에 사용하세요.)
re.VERBOSE 사용하기
이제 여러분은 아마 정규식이 매우 작은 표기지만, 대단히 읽기 쉽진 않음을 알 겁니다. 보통 복잡도의 REs는 그들을 읽고 이해하기 어렵게 만드는, 백슬래시, 괄호, 그리고 메타 문자들의 긴 모음이 될 수 있습니다.
이런 REs를 위해, 정규식을 컴파일 할 때 re.VERBOSE
기호를 지정하는 것은 유용할 수 있습니다. 왜냐하면, 이는 여러분이 정규식을 더 명확히 형식화하도록 해주기 때문입니다.
re.VERBOSE
기호는 여러 효과를 가집니다. 문자 클래스 안에 있지 않은 정규식 내의 공백 문자는 무시됩니다. 이는 dog | cat
같은 식이 더 짧은 읽기 쉬운 dog|cat
와 동등하지만, [a b]
는 여전히 문자 'a'
, 'b'
, 또는 스페이스와 맞음을 의미합니다. 게다가 여러분은 RE 내부에 주석을 달 수도 있습니다; 주석은 #
에서 다음 개행문자까지 확장합니다. 세개의 괄호 문자열을 사용할 때, 이는 RE를 더 깔끔하게 형식화할 수 있도록 합니다:
pat = re.compile(r""" s* # Skip leading whitespace (?P<header>[^:]+) # Header name s* : # Whitespace, and a colon (?P<value>.*?) # The header's value -- *? used to # lose the following trailing whitespace s*$ # Trailing whitespace to end-of-line """, re.VERBOSE)
이는 다음보다 훨씬 읽기 쉽습니다:
pat = re.compile(r"s*(?P<header>[^:]+)s*:(?P<value>.*?)s*$")
피드백
정규식은 복잡한 주제입니다. 이 문서가 이를 이해하는데 도움이 되었나요? 분명하지 않은 부분이 있었거나, 여러분이 부딪힌 여기서 다루지 않은 문제들이 있었나요? 그렇다면, 저자에게 개선을 위해 제안을 보내주세요.
정규식 상의 더 완전한 책은 거의 확실히 제프리 프라이들(Jeffrey Friedl)의 오’레일리(O’Reilly)에서 나온 정규식 마스터하기(Mastering Regular Expressions) 입니다. 불행히도, 그건 오로지 정규식의 Perl과 Java 맛에 집중하고, 파이썬의 것은 전혀 들어있지 않습니다. 그래서 파이썬 프로그래밍의 레퍼런스로는 유용하지 않을 수 있습니다. (첫번째 판은 파이썬의 여러분을 많이 도와주진 않을, 지금은 없어진 regex
모듈을 다뤘었습니다.) 여러분의 도서관에서 구할 수 있는지를 고려해 보세요.
[Notepad++] Hexa string을 0x??로 정규식으로 replace
검색모드를 “정규 표현식(E)”로 두고,
찾을 내용에 “([0-9A-Fa-f]{2})”
바꿀 내용에 “0x\1, ”
MacOS에서는 TextWrangler,
Linux에서는 Kate를..
[Perl] 정규식으로 문자열 찾기
#!/usr/bin/perl -w open(F, "cl.txt"); @strings = <F>; sub getIssue { foreach $line (@strings) { $lihttps://ko.perlmaven.com/matching-numbers-using-perl-regexne =~ s/^\s+//; $line =~ s/\s+$//; if ($line =~ /\[[I|i][S|s][S|s][U|u][E|e]\s*[#]*\]\s*([A-Za-z0-9_.\-]+)/) { $desc = $1; return $desc; } $desc; }; $issue = &getIssue(); close(F);
.* 가 모든 문자.
참고 : https://ko.perlmaven.com/matching-numbers-using-perl-regex
구글의 Python Tutorial
[번역] 구글의 파이썬 튜토리얼 요약.
원문은 Google’s Python Class(https://developers.google.com/edu/python/)
* Python 환경 설정
나올 때 윈도우에서는 Ctrl-Z, 다른 OS에서는 Ctrl-D
* Introduction
if __name__ == ‘__main__’:
main()
sys.argv[0]는 프로그램 이름, sys.argv[1]은 첫번째 인자, sys.argv[2]는 두번째 인자…
TAB 문자 대신 Space들을 입력.
help(len) : len 함수에 대한 문서를 출력
dir(sys): sys 모듈에 대한 전반적인 문서
help(sys.exit): sys 내의 exit() 함수에 대한 문서
* Strings
파이썬 스트링은 “immutable”하다. 즉, 생성 후에 값을 변경할 수 없다. 따라서 다른 값이 되면 새로운 스트링이 생성된다.
“”” 로 여러줄 string 가능
[ ]로 문자 접근 가능
Slice로 substring 얻기
len(string) 은 string의 길이. sequence 타입에는 다 사용 가능
‘+’ 로 concatenate.
Value를 string 으로 변경하고 싶을 때는 str() 함수를 사용
pi = 3.14
text = ‘The value of pi is ‘ + str(pi)
정수로 나눗셈을 할 때는 // 를 사용. 6 // 5 == 1
print 뒤에 줄바꿈을 하고 싶지 않으면 마지막에 , 를 붙인다.
r 을 앞에 붙이면 ‘raw’ string. \ 를 특별히 취급하지 않는다. r’x\nx’ 는 ‘x\nx’ 가 됨. u 를 앞에 붙이면 unicode.
s.lower(), s.upper(): 소문자, 대문자로
s.strip(): 앞 뒤의 whitespace 제거
s.isalpha() / s.isdigit() / s.isspace(): 알파벳인지, 숫자인지, space인지
s.startswith(‘other’), s.endswith(‘other’): s 스트링이 다른 특정 스트링으로 시작하거나 끝나는지.
s.find(‘other’): s 스트링 안의 다른 스트링을 찾는다. 찾으면 인덱스 리턴, 못찾으면 -1
s.replace(‘old’, ‘new’): 모든 ‘old’를 ‘new’로 변경한 string 리턴
s.split(‘delim’): 주어진 delimiter로 자른 substring 리스트를 리턴,
s.join(list): split()의 반대, s를 delimiter로 리스트를 합친다.
길이가 1인 string 에 ==, <= 등의 작업을 할 수 있다. char 타입이 따로 없다.
H e l l o
0 1 2 3 4
-5 -4 -3 -2 -1
s[1:4]는 ‘ell’, s[1:]는 ‘ello’, s[:]는 ‘Hello’의 복사본, s[1:100]은 ‘ello’ – 더 큰 index를 사용하면 string의 length나 마찬가지가 된다.
s[-1]은 ‘o’, s[-4]은 ‘e’, s[:-3]은 ‘He’, s[-3:]은 ‘llo’
s[:n] + s[n:] == s 는 항상 참. n이 음수거나, index영역을 벗어났을 때도 성립.
% 연산자를 사용해 printf 같은 format string. %d는 int, %s는 string, %f/%g는 float, 오른쪽에 튜플.
text = “%d little pigs come out or I’ll %s and %s and %s” % (3, ‘huff’, ‘puff’, ‘blow down’)
너무 길면 전체 expression을 괄호로 싸면 된다.
text = (“%d little pigs come out or I’ll %s and %s and %s” %
(3, ‘huff’, ‘puff’, ‘blow down’))
unistring.encode()와 unicode로 encoding 변환.
s = unistring.encode(‘utf-8’)
t = unicode(s, ‘utf-8’)
t == unistring
if / elif/ else. “zero” 값들(None, 0, “”, ”, [], {}, False)은 false, True/False로 boolean 값, and, or, not 사용. ()로 expression 을 감싸지 않는다.
* Lists
= 을 사용하면 그냥 reference.
‘+’ 를 사용해 list를 합칠 수 있다.
for와 in 으로 iteration
squares = [1, 4, 9, 16]
sum = 0
for num in squares:
sum += num
print sum ## 30
in 은 독립적으로 어떤 원소가 list(또는 다른 collection)에 있는지 테스트 할 수 있다.
list = [‘larry’, ‘curly’, ‘moe’]
if ‘curly’ in list:
print ‘yay’
range(n)은 0, 1, 2, …, n-1 까지의 숫자를 리턴. range(a, b) 는 a, a+1, a+2, …, b-1 까지를 리턴.
for i in range(100):
print i
xrange()는 전체 리스트를 만드는 과정을 없애 성능 상 좋다.
while
i = 0
while i < len(a):
print a[i]
i = i + 3
list.append(elem): 리스트를 수정하여 가장 뒤에 elem 추가. 리턴하지 않는다.
list.insert(index, elem): index에 elem을 추가. 리턴하지 않는다.
list.extend(list2): list2의 원소들을 list에 추가. 리턴하지 않는다.
list.index(elem): elem이 있으면 index 리턴, 없으면 ValueError를 발생. ValueError 없이 확인하려면 in 을 사용.
list.remove(elem): 첫번째 elem을 제거, 없으면 ValueError. 리턴하지 않는다.
list.sort(): list를 sort. 리턴하지 않는다. sorted()를 더 자주 사용.
list.reverse(): list를 역순으로 변경. 리턴하지 않는다.
list.pop(index): index의 원소를 제거하고 리턴. index를 생략하면 가장 끝의 원소를 리턴(append()와 반대)
* Sorting
sorted()를 사용. reverse=True를 전달하면 역순. Case sensitive하다.
strs = [‘aa’, ‘BB’, ‘zz’, ‘CC’]
print sorted(strs) ## [‘BB’, ‘CC’, ‘aa’, ‘zz’]
print sorted(strs, reverse=True) ## [‘zz’, ‘aa’, ‘CC’, ‘BB’]
key로 함수를 전달하면 해당 함수를 key로 정렬. key=len, key=str.lower 하면 대소문자 동일하게 취급하여 정렬.
print sorted(strs, key=str.lower) ## [‘aa’, ‘BB’, ‘CC’, ‘zz’]
key로 custom 함수 전달 가능
def MyFn(s):
return s[-1]
print sorted(strs, key=MyFn)
cmp=cmpFn 선택 인자를 전달할 수도 있음. 내장 함수는 cmp(a, b)로 -/0/+ 로 순서를 리턴
Tuple은 struct와 비슷한 역할. 변경 불가능, 크기가 변하지 않음. () 를 통해 만듦. 크기가 1인 튜플은 ( 1, ) 처럼 , 을 넣어 만듦.
변수 할당에 사용 가능. 반환값이 여러 값을 가진 경우도 사용 가능
(x, y, z) = (42, 13, “Hike”)
(err_string, err_code) = foo()
[ _expr_ for var in list ] 형태로 원하는 형태의 리스트 생성 가능.
fruits = [‘apple’, ‘cherry’, ‘banana’, ‘lemon’]
afruits = [ s.upper() for s in fruits if ‘a’ in s ] ## [‘APPLE’, ‘BANANA’]
* Dictionaries and Files
dict = {}
dict[‘a’] = ‘alpha’
dict[‘g’] = ‘gamma’
dict[‘o’] = ‘omega’
print dict[‘a’] ## ‘alpha’
dict[‘a’] = 6
‘a’ in dict ## True
## print dict[‘z’] ## Throws KeyError
if ‘z’ in dict: print dict[‘z’] ## KeyError를 피한다.
print dict.get(‘z’) ## None
dict.get(key, not-found) 형태로 키가 없을 경우 not-found 로 설정한 값을 리턴하도록 할 수도 있다.
dict.keys(): key 리스트
dict.values(): value 리스트
dict.items(): (key, value) 튜플의 리스트
for k, v in dict.items(): print k, ‘>’, v
iterkeys(), itervalues(), iteritems()는 전체 리스트를 만들지 않아 성능 상 좋다.
hash = {}
hash[‘word’] = ‘garfield’
hash[‘count’] = 42
s = ‘I want %(count)d copies of %(word)s’ % hash # ‘I want 42 copies of garfield’
del 로 변수, list 원소, dict key/value를 지울 수 있다.
Files open(), close(). open 시 ‘rU’를 사용하면 줄바꿈을 ‘\n’으로 변형하여 준다.
f = open(‘foo.txt’,’rU’)
for line in f: ## 파일을 한 줄씩 방문
print line, ## line 끝에 이미 줄바꿈이 포함되어 있으므로 print가 줄바꿈하지 않도록 한다.
f.close()
f.readlines()는 전체를 메모리에 올리고 줄들의 list를 리턴, read()는 전체 파일을 하나의 string으로.
파일에 쓰려면, f.write(string). print를 사용하려면, print >> f, string. python 3에서는 print(string, file=f)
codes는 unicode를 읽을 때 사용 가능
import codecs
f = codecs.open(‘foo.txt’, ‘rU’, ‘utf-8’)
for line in f: # line은 unicode string이 됨.
* Regular expression
import re
str = ‘an example word:cat!!’
match = re.search(r’word:\w\w\w’, str)
if match:
print ‘검색 성공’, match.group() ## 발견 word:cat
else
print ‘검색 실패’
패턴 스트링은 항상 r로 시작.
a, X, 9: 있는 그대로의 문자를 매치
.: 아무 문자 하나를 매치. \n 은 제외
\w: 하나의 word에 쓰이는 문자 하나를 매치(단어가 아님). a-z, A-Z, 0-9, _ [a-zA-Z0-9_]를 매치.
\W: non-word에 쓰이는 문자 하나를 매치
\b: word와 non-word의 경계
\s: 하나의 whitespace 문자[ \n\r\t\f]를 매치
\S: whitespace 이외의 문자를 매치
\d: 숫자 [0-9]
^: 시작
$: 끝
\를 붙여 위의 것들을 그저 문자로 사용 가능
+: 왼쪽 패턴 1개 이상.
*: 왼쪽 패턴 0개 이상.
?: 왼쪽 패턴 0개 혹은 1개
+와 *는 가장 왼쪽의 것을 찾고, greedy 하다.
r'[\w.-]+@[\w.-]+’ 로 @ 주위에 ‘.’와 ‘-‘도 매치하도록 할 수 있다. -가 구간을 매치하지 않도록 하려면 가장 마지막에 넣는다.
[] 안의 내용을 ^로 시작하면 집합을 뒤집는다. [^ab]는 ‘a’와 ‘b’를 제외한 모든 문자를 뜻한다.
패턴 안에 ( )를 넣으면 그룹으로 구분할 수 있게 해준다.
str = ‘purple alice-b@google.com monkey dishwasher’
match = re.search(r'([\w.-]+)@([\w.-]+)’, str)
if match
print match.group() ## ‘alice-b@google.com’
print match.group(1) ## ‘alice-b’
print match.group(2) ## ‘google.com’
findall(): 모든 패턴에 해당하는 string 리스트를 리턴
f = open(‘text.txt’, ‘r’)
strings = re.findall(r’some pattern’, f.read())
findall 에 ( )를 넣으면 그룹에 해당하는 튜플 리스트를 만든다.
str = ‘purple alice@google.com, blah monkey bob@abc.com blah dishwasher’
tuples = re.findall(r'(\w\.-]+)@([\w\.-]+)’, str)
print tuples ## [(‘alice’, ‘google.com’), (‘bob’, ‘abc.com’)]
for tuple in tuples:
print tuple[0] ## username
print tuple[1] ## host
re.search(pat, str, re.IGNORECASE) 처럼 추가 옵션 가능
IGNORECASE: 대소문자 구별하지 않음
DOTALL: 마침표 . 이 newline을 매치하도록 한다. 보통은 newline을 제외한 모든것들을 매치.
MULTILINE: 여러줄로 이루어진 string에서 ^와 $가 줄의 시작과 끝을 매치하도록 한다. 보통은 ^ $는 전체 스트링의 시작과 끝을 매치한다.
정규 표현식의 확장으로 .*? 나 .+?처럼 ?를 끝에 더해서 greedy 하지 않게 변경할 수 있다.
str = ‘<b>foo</b> and <i>so on</i>’
match = re.search(r'(<.*>)’, str)
if match:
print match.group() ##'<b>foo</b> and <i>so on</i>’
match = re.search(r'(<.*?>)’, str)
if match:
print match.group() ## ‘<b>’
re.sub(pat, replacement, str) 은 str에서 pat과 매치되는 모든 스트링들을 찾아 replacement로 치환한다. replacement 스트링은 \1, \2 를 이용하여 group(1), group(2)를 표현할 수 있다.
str = ‘purple alice@google.com, blah monkey bob@abc.com blah dishwasher’
print re.sub(r'([\w\.-]+)@([\w\.-]+)’, r’\1@yo-yo-dyne.com’, str)
# purple alice@yo-yo-dyne.com, blah monkey bob@yo-yo-dyne.com blah dishwasher
* Utilities
os 모듈
filenames = os.listdir(dir): dir에 있는 .과 ..을 제외한 파일이름들의 리스트, 절대경로가 아님.
os.path.join(dir, filename): filename과 dir을 합쳐서 path를 만든다.
os.path.abspath(path): path를 받아서 절대 경로를 리턴
os.path.dirname(path), os.path.basename(path): dir/foo/bar.html을 받아서 dirname ‘dir/foo’와 basename’bar.html’을 리턴
os.path.exists(path): path 가 존재하면 True를 리턴
os.mkdir(dir_path): dir 하나를 만든다.
os.makedirs(dir_path): dir_path를 만들기 위한 모든 디렉토리들을 만든다.
shutil.copy(source-path, dest-path): 파일을 복사한다. 복사될 디렉토리가 존재해야 한다.
commands 모듈: 외부 명령을 실행하고 결과물을 얻어옴.
(status, output) = commands.getstatusoutput(cmd): cmd를 실행하고 exit할 때까지 기다려서,
status int값과 output text를 tuple로 리턴한다. stdout과 stderr이 하나의 출력물로 합쳐져서 나타난다.
output = commands.getoutput(cmd): 위와 동일, status값을 받지 않는다는 것만 다름.
commands.getstatus() 라는 함수는 사용하지 말아라.
sub-process에 대한 더 많은 제어권을 원하면 “popen2” 모듈을 보라.
os.system(cmd): cmd의 결과물을 여러분 코드의 output으로 출력하고, error 코드를 리턴.
try/except. ‘except IOError, e:’ 의 형태로 exception 객체에 대한 포인터를 얻을 수 있다.
try:
f = open(filename, ‘rU’)
text = f.read()
f.close()
except IOError:
sys.stderr.write(‘problem reading:’ + filename)
urllib 모듈: url을 마치 file처럼 읽을 수 있게 한다. urlparse 모듈은 url을 분해하거나 합치는 기능을 제공
ufile = urllib.urlopen(url): url에 대한 file 같은 객체를 리턴
text = ufile.read(): 파일처럼 읽음. readlines()도 사용 가능
info = ufile.info(): 요청에 대한 meta 정보를 리턴. info.gettype() 으로 ‘text/html’ 같은 mime 타입을 얻어 옴.
baseurl = ufile.geturl(): 요청에 대한 “base” url을 얻어옴. redirect로 인해 open할 때 사용한 url과 다를 수 있다.
urllib.urlretrieve(url, filename): url의 데이터를 다운받아 filename의 file로 저장한다.
urlparse.urljoin(baseurl, url): full url을 만든다.
def wget2(url):
try:
ufile.urllib.urlopen(url)
if ufile.info().gettype() == ‘text/html’:
print ufile.read()
except IOError:
prit ‘problem reading url:’, url