Linux나 Mac을 사용하는 경우 터미널을 열 때마다 쉘 애플리케이션을 사용하는 것입니다.
쉘은 시스템에서 명령을 실행하는 데 도움이 되는 인터페이스입니다.
쉘은 환경 변수를 호스트하고 명령 기록 및 자동 완성과 같은 유용한 기능을 가지고 있습니다.
내부적으로 작동하는 방식을 배우는 것을 좋아하는 사람이라면 이 게시물이 적합할 것입니다!
쉘은 어떻게 작동합니까?
자체 쉘 애플리케이션을 구축하기 위해 쉘이 실제로 무엇인지 생각해 보겠습니다.
먼저, 일반적으로 현재 사용자 및 현재 디렉토리와 같은 추가 정보가 포함된 프롬프트가 표시됩니다. 그런 다음 명령을 입력하고 Enter 키를 누르면 결과가 화면에 표시됩니다.
네, 아주 기본적인 것 같지만 뭔가 생각나지 않습니까?
pry
를 생각하고 있다면 그렇다면 당신이 옳습니다!
기본적으로 운영 체제에 대한 REPL(Read-Eval-Print-Loop)의 셸입니다.
쉘의 첫 번째 버전을 작성할 수 있음을 알고 있습니다. :
prompt = "> " print prompt while (input = gets.chomp) break if input == "exit" system(input) print prompt end
이것은 우리에게 최소한의 기능적 쉘을 제공할 것입니다. 다른 많은 REPL과 유사한 응용 프로그램에서 사용하는 라이브러리를 사용하여 이를 개선할 수 있습니다.
해당 라이브러리를 Readline
이라고 합니다. .
Readline 라이브러리 사용
Readline은 Ruby 표준 라이브러리의 일부이므로 설치할 것이 없습니다. require
만 있으면 됩니다. 그것.
Readline
사용의 장점 중 하나 명령 기록을 자동으로 유지할 수 있다는 것입니다.
또한 명령 프롬프트 및 기타 여러 작업을 인쇄할 수 있습니다.
다음은 Readline
을 사용하는 셸의 v2입니다. :
require 'readline' while input = Readline.readline("> ", true) break if input == "exit" system(input) end
훌륭합니다. 두 개의 puts
을 없앴습니다. 프롬프트 및 이제 Readline
에서 몇 가지 강력한 기능에 액세스할 수 있습니다. . 예를 들어, 키보드 단축키를 사용하여 단어를 삭제할 수 있습니다(CTRL + W
) 또는 기록 검색(CTRL + R
) )!
전체 기록을 인쇄하는 새 명령을 추가해 보겠습니다.
require 'readline' while input = Readline.readline("> ", true) break if input == "exit" puts Readline::HISTORY.to_a if input == "hist" # Remove blank lines from history Readline::HISTORY.pop if input == "" system(input) end<블록 인용>
재미있는 사실:pry에서 이 코드를 시도하면 pry의 명령 기록을 얻을 수 있습니다! 그 이유는 pry가 Readline
도 사용하기 때문입니다. 및 Readline::HISTORY
공유 상태입니다.
이제 hist
를 입력할 수 있습니다. 명령 기록을 얻으려면 🙂
자동 완성 추가
좋아하는 쉘의 자동 완성 기능 덕분에 많은 입력을 줄일 수 있습니다. Readline을 사용하면 이 기능을 셸에 쉽게 통합할 수 있습니다.
기록에서 명령을 자동 완성하여 시작하겠습니다.
예 :
comp = proc { |s| Readline::HISTORY.grep(/^#{Regexp.escape(s)}/) } Readline.completion_append_character = " " Readline.completion_proc = comp ## rest of the code goes here ##
이 코드를 사용하면 <tab>
를 눌러 이전에 입력한 명령을 자동 완성할 수 있습니다. 열쇠. 이제 한 단계 더 나아가 디렉토리 자동 완성 기능을 추가해 보겠습니다.
예 :
comp = proc do |s| directory_list = Dir.glob("#{s}*") if directory_list.size > 0 directory_list else Readline::HISTORY.grep(/^#{Regexp.escape(s)}/) end end
completion_proc
가능한 후보 목록을 반환합니다. 이 경우 Dir.glob
을 사용하여 입력된 문자열이 디렉터리 이름의 일부인지 확인하기만 하면 됩니다. . 나머지는 Readline에서 처리합니다!
시스템 메소드 구현
이제 25줄의 코드로 나쁘지 않은 기록 및 자동 완성 기능이 있는 작동하는 셸이 있어야 합니다. 🙂
그러나 실제로 명령을 실행하는 장면 뒤에서 무슨 일이 일어나고 있는지에 대한 통찰력을 얻을 수 있도록 더 깊이 파고들고 싶은 것이 있습니다.
이것은 system
에 의해 수행됩니다. 메서드, C에서 이 메서드는 명령을 /bin/sh
로 보냅니다. , 이것은 셸 응용 프로그램입니다. /bin/sh
Ruby에서 수행합니다.
참고 :이것은 Linux / Mac에서만 작동합니다 🙂
시스템 방법:
def system(command) fork { exec(command) } end
여기서 일어나는 일은 fork
현재 프로세스의 새 복사본을 만든 다음 이 프로세스는 exec
를 통해 실행하려는 명령으로 대체됩니다. 방법. 이것은 Linux 프로그래밍에서 매우 일반적인 패턴입니다.
포크하지 않으면 현재 프로세스가 대체됩니다. 즉, 실행 중인 명령(ls
, cd
또는 기타)가 완료되면 Ruby 프로그램이 종료됩니다.
여기에서 그 일이 일어나는 것을 볼 수 있습니다:
def system(command) exec(command) end system('ls') # This code will never run! puts "after system"
결론
이 게시물에서 쉘이 REPL과 유사한 인터페이스(irb
/ pry
) 시스템과 상호 작용합니다. 또한 강력한 Readline
을 사용하여 자신만의 셸을 만드는 방법도 배웠습니다. 기록 및 자동 완성과 같은 많은 내장 기능을 제공하는 라이브러리(하지만 작동 방식을 정의해야 함).
그리고 그 후에 fork
에 대해 배웠습니다. + exec
Linux 프로그래밍 프로젝트에서 일반적으로 사용되는 패턴입니다.
이 게시물이 마음에 드셨다면 저에게 호의를 베풀고 모든 Ruby 친구들과 공유해 주시겠습니까? 블로그 성장에 도움이 되고 더 많은 사람들이 배울 수 있습니다 🙂