Shellwords 모듈
Shellwords 모듈
Shellwords는 문자열을 UNIX Bourne 셸의 단어 파싱 규칙에 맞춰 다루는 모듈이에요. 명령줄을 안전하게 분리하거나, 반대로 여러 인자를 다시 명령줄 문자열로 조립할 때 씁니다.
출처: Ruby 4.0 API
본문
이 모듈은 UNIX Bourne 셸의 단어 파싱 규칙에 따라 문자열을 조작해요.
shellwords() 함수는 원래 shellwords.pl을 포팅한 것이었는데, IEEE Std 1003.1-2008, 2016 Edition의 Shell & Utilities 볼륨에 맞게 수정됐어요.
사용법 (Usage)
Shellwords로 문자열을 Bourne 셸 친화적인 배열로 파싱할 수 있어요.
require 'shellwords'
argv = Shellwords.split('three blind "mice"')
argv #=> ["three", "blind", "mice"]
큰따옴표로 감싼 "mice"는 하나의 토큰으로 남는 걸 볼 수 있어요.
Shellwords를 require한 뒤에는 String#shellsplit이라는 별칭도 쓸 수 있어요.
argv = "see how they run".shellsplit
argv #=> ["see", "how", "they", "run"]
따옴표는 특수 문자로 취급되기 때문에, 짝이 맞지 않는 따옴표가 있으면 ArgumentError가 발생해요.
argv = "they all ran after the farmer's wife".shellsplit
#=> ArgumentError: Unmatched quote: ...
반대로 작동하는 메서드도 있어요. Shellwords.escape, 또는 그 별칭인 String#shellescape는 문자열 안의 셸 메타문자를 이스케이프해서 명령줄에 안전하게 쓸 수 있게 해줘요.
filename = "special's.txt"
system("cat -- #{filename.shellescape}")
# runs "cat -- special\\'s.txt"
여기서 --(–)를 주목할게요. 이게 없으면 cat(1)이 다음 인자가 -로 시작할 때 명령줄 옵션으로 오해할 수 있어요. Shellwords.escape는 문자열을 Bourne 셸이 다시 원래 문자열로 파싱하는 형태로 바꿔주는 게 보장되지만, 임의의 인자를 명령에 넘기는 게 안전한지는 프로그래머 몫이라는 점을 꼭 기억하세요.
Shellwords에는 배열용 코어 확장, Array#shelljoin도 있어요.
dir = "Funny GIFs"
argv = %W[ls -lta -- #{dir}]
system(argv.shelljoin + " | less")
# runs "ls -lta -- Funny\\ GIFs | less"
이 메서드로 인자 배열을 완전한 명령줄 하나로 만들 수 있어요.
Public Class Methods
escape(str)
문자열을 Bourne 셸 명령줄에서 안전하게 쓰일 수 있도록 이스케이프해요. str은 to_s에 응답하는 문자열이 아닌 객체일 수도 있어요.
exec 시스템 콜의 특성상 str에 NUL 문자는 들어가면 안 돼요.
결과 문자열은 인용 부호 없이 사용하도록 만들어진 것이라, 큰따옴표 안이나 작은따옴표 안에서 쓰려는 의도가 아니라는 점에 유의하세요.
argv = Shellwords.escape("It's better to give than to receive")
argv #=> "It\\'s\\ better\\ to\\ give\\ than\\ to\\ receive"
String#shellescape는 이 함수의 줄임 표현이에요.
argv = "It's better to give than to receive".shellescape
argv #=> "It\\'s\\ better\\ to\\ give\\ than\\ to\\ receive"
# Search files in lib for method definitions
pattern = "^[ \\t]*def "
open("| grep -Ern -e #{pattern.shellescape} lib") { |grep|
grep.each_line { |line|
file, lineno, matched_line = line.split(':', 3)
# ...
}
}
문자열을 사용할 셸 환경에 맞는 인코딩으로 만들어 두는 건 호출자의 책임이에요.
멀티바이트 문자는 바이트가 아니라 멀티바이트 문자 그대로 취급돼요.
str의 길이가 0이면 빈 인용 문자열("")을 반환해요.
join(array)
인자 목록 array에서 명령줄 문자열을 만들어요.
모든 요소를 공백으로 구분해 하나의 문자열로 연결하되, 각 요소는 Bourne 셸용으로 이스케이프하고 to_s로 문자열화해요. Shellwords.shellescape도 함께 보세요.
ary = ["There's", "a", "time", "and", "place", "for", "everything"]
argv = Shellwords.join(ary)
argv #=> "There\\'s a time and place for everything"
Array#shelljoin은 이 함수의 지름길이에요.
ary = ["Don't", "rock", "the", "boat"]
argv = ary.shelljoin
argv #=> "Don\\'t rock the boat"
Array#join에서 허용되듯이 요소에 문자열이 아닌 객체를 섞어 넣을 수 있어요.
output = `#{['ps', '-p', $$].shelljoin}`
split(line)
문자열을 UNIX Bourne 셸이 하는 것과 같은 방식으로 토큰 배열로 분리해요.
argv = Shellwords.split('here are "two words"')
argv #=> ["here", "are", "two words"]
exec 시스템 콜의 특성상 line에 NUL 문자는 들어가면 안 돼요.
다만 이것은 명령줄 파서가 아니라는 점을 유의하세요. 작은따옴표·큰따옴표·백슬래시를 제외한 셸 메타문자는 특별히 취급되지 않아요.
argv = Shellwords.split('ruby my_prog.rb | less')
argv #=> ["ruby", "my_prog.rb", "|", "less"]
|도 그냥 일반 토큰으로 남는 걸 볼 수 있어요.
String#shellsplit은 이 함수의 지름길이에요.
argv = 'here are "two words"'.shellsplit
argv #=> ["here", "are", "two words"]