Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

25줄의 Ruby 코드로 쉘 작성하기

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 친구들과 공유해 주시겠습니까? 블로그 성장에 도움이 되고 더 많은 사람들이 배울 수 있습니다 🙂