소소한 유틸리티 스크립트를 작성하는 것은 재미있지만, 때로는 정말 정직한 명령줄 응용 프로그램을 작성해야 할 필요가 있습니다. 인수를 취하고 입력, 출력, 오류 보고 등에 대한 유닉스 규칙과 잘 어울리는 것입니다.
다행히 Ruby는 명령줄 응용 프로그램에 필요한 모든 구성 요소를 상당히 쉽게 제공합니다. 이 게시물에서 저는 일반적인 "젬 Y로 X를 수행하는 방법" 접근 방식을 넘어 일류 명령줄 앱을 만들기 위해 함께 사용되는 모든 부분에 대한 광범위한 개요를 제공하고자 합니다.
입력:환경 변수
환경 변수는 일반적으로 잠시 동안 유지하려는 짧은 구성 값에 사용됩니다. API 키가 좋은 예입니다.
환경 변수 설정
사용자는 일반적으로 bash와 같은 셸을 통해 환경 변수를 설정합니다. 특정 세션에 대해 설정할 수 있습니다.
$ AWS_ACCESS_KEY_ID=FOO
현재 세션과 새 bash 세션에 대해 설정할 수 있습니다.
$ export AWS_ACCESS_KEY_ID=FOO
또는 단일 프로그램에 대해 설정할 수 있습니다.
$ env AWS_ACCESS_KEY_ID=FOO aws s3 mb s3://mybucket
환경 변수 읽기
사용자가 환경 변수를 어떻게 설정하든 관계없이 Ruby의 ENV
를 통해 읽을 수 있습니다. 해시.
key_id = ENV["AWS_ACCESS_KEY_ID"]
환경 변수는 내부적으로 어떻게 작동합니까?
모든 프로세스에는 연관된 환경 변수 테이블이 있습니다. 자식 프로세스를 생성할 때 부모가 변경하려는 모든 변경 사항과 함께 해당 테이블의 복사본을 얻습니다.
진흙처럼 맑습니까?
더 읽기 쉬운 설명을 보려면 내 다른 블로그 게시물인 Rubyist's Guide to Environment Variables를 확인하세요.
명령줄 인수
명령줄 인수는 터미널에서 프로그램을 실행할 때 프로그램 이름 뒤에 넣는 모든 것입니다.
$ echo foo bar baz
위의 예에서 "foo", "bar" 및 "baz"는 모두 명령줄 인수입니다.
보다 현실적인 예는 다음과 같습니다.
$ honeybadger deploy -v --environment staging
그러나 여기에서도 명령줄 인수는 텍스트일 뿐입니다. --environment staging
를 원하는 경우 의미 무엇이든 우리 스스로 해결해야 합니다.
명령줄 인수 보내기
다음과 같이 전송된 인수를 이미 보았습니다.
$ echo foo bar baz
하지만 Ruby는 인터프리터 언어이기 때문에 다음과 같은 내용을 접할 수도 있습니다.
$ ruby myprog.rb foo
OS에 관한 한 "ruby"라는 프로그램을 실행하고 두 개의 인수를 전달합니다.
운 좋게도 Ruby는 "foo" 인수가 Ruby 자체가 아니라 프로그램을 위한 것임을 알 만큼 똑똑합니다. 따라서 프로그램에는 "foo"
라는 하나의 인수가 표시됩니다. .
명령줄 인수 읽기
명령줄 인수는 ARGV
라는 배열에 저장됩니다. . 전역 변수이므로 프로그램의 어디에서나 액세스할 수 있습니다.
아래 예에서는 ARGV
의 모든 내용을 인쇄하고 있습니다. . 이것을 자유롭게 복사하여 터미널에 붙여넣고 조금 가지고 놀아보세요.
$ ruby -e "puts ARGV.inspect" foo bar baz
["foo", "bar", "baz"]
<블록 인용>
ruby -e
에 휩쓸리지 마십시오. 사업. 여기서는 프로그램과 실행 결과를 한 줄로 표시할 수 있기 때문에 여기에서만 사용하고 있습니다.
명령줄 인수 구문 분석
명령줄 인수는 텍스트일 뿐입니다. 텍스트가 의미가 되도록 하려면 무엇이든, 당신은 그것을 구문 분석해야 할 것입니다. 다행히도 구문 분석에 도움이 될 수 있는 몇 가지 좋은 라이브러리가 있습니다.
수년에 걸쳐 명령줄 인수에 대한 다소 표준적인 구문이 등장했습니다. 다음과 같이 보입니다.
$ program -a --option foo
이 스타일을 사용하면 -h
와 같은 부울 플래그를 사용할 수 있습니다. 도움말을 표시합니다. 또한 값으로 옵션을 지정할 수 있습니다.
OptionParser
소개
OptionParser
class는 Ruby의 표준 라이브러리의 일부입니다. 위의 스타일과 일치하는 옵션을 쉽게 구문 분석할 수 있는 방법을 제공합니다.
Hello라는 간단한 응용 프로그램을 만들어 보겠습니다. 명령줄 인수를 통해 이름을 지정할 수 있습니다.
require 'optparse'
# This will hold the options we parse
options = {}
OptionParser.new do |parser|
# Whenever we see -n or --name, with an
# argument, save the argument.
parser.on("-n", "--name NAME", "The name of the person to greet.") do |v|
options[:name] = v
end
end.parse!
# Now we can use the options hash however we like.
puts "Hello #{ options[:name] }" if options[:name]
그런 다음 실행하면 제대로 작동합니다.
$ ruby hello.rb --name Starr
Hello Starr
$ ruby hello.rb -n Starr
Hello Starr
"도움말" 화면 추가
도움말 기능을 추가하는 것도 간단합니다. 우리가 해야 할 일은 텍스트를 제공하고 -h
에 대한 명령을 추가하는 것입니다. :
OptionParser.new do |parser|
parser.banner = "Usage: hello.rb [options]"
parser.on("-h", "--help", "Show this help message") do ||
puts parser
end
...
end.parse!
이제 도움을 요청할 수 있습니다.
$ ruby hello.rb -h
Usage: hello.rb [options]
-h, --help Show this help message
-n, --name NAME The name of the person to greet.
타입캐스팅 인수
모든 명령줄 인수는 문자열이지만 때때로 사용자가 숫자나 날짜를 제공하기를 원할 수 있습니다. 수동으로 변환하는 것은 지루할 수 있으므로 OptionParser
당신을 위해 그것을합니다.
아래 예에서 사용자가 프로그램에서 인사를 몇 번이나 해야 하는지 지정할 수 있는 "count" 옵션을 추가하겠습니다. OptionParser
Integer
로 형변환 , 그렇게 합니다.
OptionParser.new do |parser|
...
# Note the `Integer` arg. That tells the parser to cast the value to an int.
# I could have used `Float`, `Date`, or a number of other types.
parser.on("-c", "--count COUNT", Integer, "Repeat the message COUNT times") do |v|
options[:count] = v
end
end.parse!
if options[:name]
options.fetch(:count, 1).times do
puts "Hello #{ options[:name] }"
end
end
이제 프로그램을 실행하면 여러 번 인사를 받습니다.
$ ruby hello.rb -n Starr -c 5
Hello Starr
Hello Starr
Hello Starr
Hello Starr
Hello Starr
이름 지정 규칙
프로그래밍의 가장 어려운 측면 중 하나는 이름을 지정하는 것입니다. 명령줄 인수도 예외는 아닙니다. 여러분을 돕기 위해 일반적인 주장과 그 의미에 대한 짧은 표를 작성했습니다.
플래그 | 일반적인 의미 |
---|---|
-a | 모두, 추가 |
-d | 디버그 모드 또는 디렉토리 지정 |
-e | 실행 또는 편집 |
-f | 파일을 지정하거나 작업을 강제 실행합니다. |
-h | 도움말 |
-m | 메시지 지정 |
-o | 출력 파일 또는 장치 지정 |
-q | 저소음 모드 |
-v | 상세 모드. 현재 버전 인쇄 |
-y | 모든 프롬프트에 "예"라고 말하세요. |
OptionParser
의 대안
OptionParser
좋긴 한데 한계가 있습니다. 가장 분명한 것은 최근 몇 년 동안 인기를 얻은 명령 기반 구문을 지원하지 않는다는 것입니다.
$ myprog command subcommand -V
다행히도 톤 옵션 구문 분석 라이브러리가 있습니다. 나는 당신이 좋아하는 것을 찾을 수 있을 것이라고 확신합니다. 다음은 흥미롭게 보이는 세 가지입니다.
- GLI - "Git-like Interface" 명령줄 파서를 사용하면 git과 같이 여러 명령으로 단일 실행 파일인 응용 프로그램을 쉽게 만들 수 있습니다.
- CRI - 중첩 명령을 지원하는 명령줄 도구를 빌드하기 위한 사용하기 쉬운 라이브러리입니다.
- Methadone - Methadone은 DSL을 구문 분석하는 자체 옵션을 제공하지만 그 이상입니다. 테스트 스위트 및 로깅을 위한 디렉토리 구조를 설정합니다. CLI용 Rails와 같습니다.
STDIN으로 더 많은 양의 데이터 입력
명령줄 인수는 더 많은 양의 데이터를 입력하는 데 적합하지 않습니다. 이를 위해 IO 스트림을 사용하고 싶을 것입니다. 그리고 STDIN은 가장 편리한 IO 스트림 중 하나입니다.
대부분의 프로그램은 운영 체제에서 자동으로 STDIN을 할당합니다. OS가 앱 데이터를 보내는 데 사용하는 읽기 전용 파일과 같습니다. 사용자로부터 키보드 입력을 받는 데 사용할 수 있습니다. 그러나 더 중요한 것은 파이프를 통해 다른 프로그램에서 데이터를 수신하는 데 사용할 수 있다는 것입니다.
읽기 전용 파일을 사용하는 것처럼 STDIN을 사용합니다. 여기에서 일부 텍스트를 내 프로그램의 STDIN에 파이핑하고 있습니다. 그런 다음 read
를 사용합니다. 가져오는 방법입니다.
$ echo "hello world" | ruby -e "puts STDIN.read.upcase"
HELLO WORLD
Ruby에서는 Enumerable
을 사용할 수 있습니다. IO 개체의 기능. 그리고 STDIN도 예외는 아닙니다. 즉, 모든 종류의 트릭을 수행할 수 있습니다.
# Get the first 20 lines
STDIN.first(20)
# Convert to integers and reject odds
STDIN.map(&:to_i).reject(&:odd)
...etc
STDOUT으로 결과 출력
STDOUT은 운영 체제가 프로그램에 할당하는 쓰기 전용 IO 스트림입니다.
STDOUT에 작성하여 사용자의 터미널에 텍스트를 보낼 수 있습니다. 사용자는 출력을 파일로 리디렉션하거나 다른 프로그램으로 파이프할 수 있습니다.
다른 쓰기 전용 파일을 사용하는 것처럼 STDOUT을 사용합니다.
STDOUT.write("Hi!\n")
# hi
물론 puts
및 print
둘 다 STDOUT으로 출력됩니다.
STDERR에 상태 정보 보내기
STDERR은 또 다른 쓰기 전용 IO 스트림입니다. 일반 출력용이 아닙니다. 이는 특히 상태 메시지를 위한 것이므로 실제 출력.
STDERR은 일반적으로 사용자가 STDOUT을 파일이나 다른 프로그램으로 리디렉션하더라도 사용자 터미널에 표시됩니다.
아래 예에서는 curl을 사용하여 웹 페이지를 가져옵니다. 웹 페이지의 내용을 STDOUT으로 출력하고 진행 정보를 STDERR로 표시합니다.
$ curl "https://www.google.com/" > /tmp/g.html
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 151k 0 151k 0 0 277k 0 --:--:-- --:--:-- --:--:-- 277k
자신의 프로그램에서 STDERR에 쓰는 것은 쉽습니다. IO 개체처럼 처리하십시오.
STDERR.write("blah\n")
STDERR.puts("blah")
예쁜 막대기로 때리기
지금까지 본 예제는 디자인 상을 받지 못할 것입니다. 하지만 명령줄 앱이 보기 흉하거나 비대화형이어야 할 이유가 없습니다.
앱을 강화하기로 결정했다면 무거운 작업을 수행할 수 있는 엄청난 보석이 있습니다. 제가 좋아하는 세 가지가 있습니다:
- highline - Highline은 사용자 입력을 수집, 검증 및 입력하는 데 많은 노력을 기울이는 훌륭한 라이브러리입니다.
- command_line_reporter - ASCII 진행 보고서를 쉽게 생성할 수 있습니다.
- 페인트 - ANSI 색상 코드를 쉽게 추가하여 지루한 오래된 텍스트를 색칠할 수 있습니다.
전체 목록 보기
종료 상태
프로그램이 오류와 함께 종료되면 종료 코드를 통해 OS에 알려야 합니다. 이것이 다음과 같은 bash 코드가 작동하는 것을 가능하게 하는 것입니다:
$ prog1 && prog2
bash의 &&
에 익숙하지 않은 경우 연산자는 단순히 "prog1이 성공적으로 종료된 경우에만 prog2를 실행합니다."를 의미합니다.
Ruby는 기본적으로 "성공" 코드로 종료되고 예외 시 "실패" 코드로 종료됩니다. 다른 모든 것은 구현하기 나름입니다. 다행히도 쉽습니다.
# Any nonzero argument to `exit` counts as failure
exit(1)
자세한 내용은 내 다른 게시물 How to exit Ruby program을 확인하세요.
프로세스 이름 설정
명령줄 프로그램이 잠시 동안 실행될 예정이라면 사람들이 시스템 프로세스를 나열할 때 프로그램이 무엇인지 아는 것이 중요합니다. 일반적으로 Ruby 프로그램 프로세스 이름은 프로그램을 실행하기 위해 입력한 모든 것으로 구성됩니다. 따라서 ruby myapp -v
를 입력했다면 , 프로세스의 이름입니다.
프로그램에 인수가 많으면 읽을 수 없게 될 수 있습니다. 따라서 보다 친숙한 프로세스 이름을 설정할 수 있습니다. 다음과 같이 할 수 있습니다.
Process.setproctitle("My Awesome Command Line App")
기분이 좋다면 프로세스 제목을 사용하여 주어진 순간에 프로세스가 수행하는 작업에 대한 정보를 사용자에게 제공할 수 있습니다.
Process.setproctitle("Mail Sender: initializing")
init(...)
Process.setproctitle("Mail Sender: connecting")
connect(...)
Process.setproctitle("Mail Sender: sending")
send(...)
자세한 내용은 내 게시물을 확인하세요:top 및 ps로 표시된 Ruby 스크립트의 프로세스 이름을 변경하는 방법