Ruby에서 ls 명령 구현

16915 단어 Rubyls

입문


안녕하세요.
DMM WEBCAMP Advent Calendar는 2020 24일째 멘토@shinooooo를 맡고 있습니다.
크리스마스가 하루 남았어요!
여러분 어떻게 지내셨어요?
사람마다 계획은 다르지만, 우선 귀중한 크리스마스 이브 시간을 내서 이 기사를 읽어 주셔서 감사합니다.
정말 감사합니다.
이 글에서 나는 루비가 설치한 ls 명령에 대한 설명과 설치를 통해 얻은 견해를 썼다.
나 자신도 루비를 공부하고 있는 사람인데, 보도 내용과 코드에 대한 조언을 받을 수 있다면 좋겠다.

개발 환경

  • macOS Catalina 10.15.7
  • Ruby 2.7.2
  • zsh 5.8
  • ls 명령


    프로그래밍을 배운 사람은 한 번의 명령을 실행한 것 같다.ls Linux 관련 문서 및 번역 웹 사이트 기능
    ls,dir,vdir- 디렉터리 내용 목록
    의 명세란 스타일에 정의된 설정입니다.
    실제 실행 시
    $ ls
    hoge huga index.html     #カレントディレクトリ内のファイルが出力される。
    
    나는 이런 표시가 나타날 것이라고 생각한다.
    이번에는 루비로 이 ls 명령을 설치합니다
    $ ruby ls.rb
    hoge huga index.html
    
    목표

    규격


    이번에 설치한 ls.rb의 규격은 다음과 같다.
  • ls, ls -l, ls -a, ls -la와 동등한 기능을 실현
  • "optparse"라는 라이브러리를 사용하기 때문에 -la 처럼 여러 옵션을 집중적으로 지정할 수 없습니다.조합할 때 입력해야 합니다-l -a.
  • ACL이라고 하는 기능(설명 생략)이 나타나지 않음
  • 실제 ls 원본 코드를 참고하지 않았기 때문에 알고리즘과 함수 이름이 다를 수 있습니다.
  • 출력 형식


    갑자기 코드 모방을 쓸 수 없기 때문에ls의 출력 분석을 진행한다.
    디렉터리와 파일을 준비하는 것은 이해하기 쉽기 때문에 다음 명령을 실행합니다.
    # directory1~directory10とfile1~file10をtestディレクトリ内に作成。
    $ mkdir test/directory{1..10} && touch test/file{1..10} 
    
    그리고 실행 ls test 출력 결과를 확인합니다.
    $ ls test
    directory1      directory3      directory6      directory9      file2           file5           file8
    directory10     directory4      directory7      file1           file3           file6           file9
    directory2      directory5      directory8      file10          file4           file7
    
    당연하다고 하면 당연하지만 예쁘게 배열되어 있다.
    먹구름에게만 수출하면 ls 처럼 가지런한 수출 결과가 나오지 않을 것 같으니 과제를 하나씩 찾아내면서 완성하는 것을 목표로 해야 한다.

    표시 순서

    ls에서 출력된 파일 이름은 다음과 같이 오름차순으로 정렬됩니다.
    파일 수, 터미널 화면 크기, 파일 이름 길이에서 1행에 표시되는 파일 수와 행 수는 약간 변경되었지만 표시 순서 에서 확인된 ls test 출력은 다음과 같습니다.
    표시 순서
    名前が1番目に若いファイル     名前が4番目に若いファイル     名前が7番目に若いファイル   ... 名前が19番目に若いファイル
    名前が2番目に若いファイル     名前が5番目に若いファイル          ︙                ... 名前が20番目に若いファイル
    名前が3番目に若いファイル     名前が6番目に若いファイル          ︙
    
    각 행名前が1,4,7,10,13,16,19番目に若いファイル,名前が2,5,8,11,14,17,20番目に若いファイル,名前が3,6,9,12,15,18番目に若いファイル을 차례로 출력하면 ls의 출력을 재현할 수 있습니다.

    처리 배열


    디렉터리의 파일 목록 Dir 클래스 을 가져오는 데 사용되며, 그룹을 통해 파일 목록을 전달합니다.이번에는 파일이라는 변수에 대입해서 처리합니다.
    그러나 수신된 수조의 내용은 승순수조 클래스sort 방법이 아니기 때문에 승순에 따라 배열된다.
    원본 방법
    files = ["file2", "file3", "file1"]
    files.sort #=> ["file1", "file2", "file3"] 
    
    배열의 아래 표시는 0부터 시작합니다.
    오름차순으로 다시 배열하면 파일에서 가장 젊은 파일 이름을 files[0] 로 설정할 수 있습니다.
    출력표시 순서에 적힌 名前が1,4,7,10,13,16,19番目に若いファイル, 출력files[0],files[3],files[6],files[9],files[12],files[15],files[18]이라고 할 수 있다.
    한 줄로 출력할 수 있는 파일 수, 줄 수를 적고 files 출력을 잘 처리하면 다음과 같이 출력됩니다.
    $ ruby ls.rb test
    directory1directory3directory6directory9file2file5file8
    directory10directory4directory7file1file3file6file9
    directory2directory5directory8file10file4file7
    
    가독성은 낮지만 한 걸음 가까워졌다ls.

    파일 이름 사이의 구분자


    그러면 어디서부터 어디까지 파일 이름인지 모르기 때문에 파일과 파일을 분리해야 한다.ls의 출력 결과의 파일 이름과 파일 이름이 어떻게 구분되는지 확인하고 싶습니다.print.rb라는 파일을 적절하게 만듭니다.
    directory1      directory3
    
    복사하여 붙여넣습니다.
    나는 이것을 문자열로 단말기에 출력하고 싶다.ls test로 둘러싸고, 앞머리에 " "print.rb
    p "directory1   directory13"
    
    지원되지 않는 문제
    터미널p에서 실행할 때 출력은 다음과 같다.
    $ ruby print.rb
    "directory1\tdirectory13"
    
    directory1과directory3 사이에 ruby print.rb 이 문자가 나타납니다.
    이것은 탭 문자를 나타냅니다.
    따라서 파일 이름이 탭 문자로 구분되어 있는지 확인할 수 있습니다.
    우리는 그것을 파일 이름의 끝에 추가할 것이다 \t.
    이를 위해 \t 방법을 사용합니다.
    여기에는 상세한 설명이 없습니다printf. 마음에 드는 사람은 읽으세요문서.
    printf 사용 예
    printf("%s\t", files[0]) # => %sの中にfiles[0]の中身が入り、出力される。
    printf("%s\t", files[1])
    # => "directory1    ""directory10   "
    
    printf에서도 다음과 같은 출력을 실현했다.
    $ ruby ls.rb test
    directory1  directory3  directory6  directory9  file2   file5   file8
    directory10 directory4  directory7  file1   file3   file6   file9
    directory2  directory5  directory8  file10  file4   file7
    
    아까와 비교하면 예뻐 보이지만 ls.rb과는 출력이 다르다.

    파일 이름 길이 통일하기


    마지막으로 예뻐 보이기 위해 파일 이름의 길이를 통일한다.lsdirectory10,file1,file2로 생각해 보세요.
    현재의 출력은 이렇다.
    directory10\tfile2 
    file1\tfile3
    
    파일 이름의 길이가 일치하지 않기 때문에 줄마다 문자 수의 편차가 발생한다.
    모든 파일 이름의 길이가 같다면
    ファイル名\tファイル名\t
    ファイル名\tファイル名\t
    
    이렇게 예뻐 보일 수 있기 때문에 공백 문자를 사용하여 글자 수가 가장 긴 파일 이름의 길이로 통일합니다.
    이번 예에서 file3 파일 이름이 가장 길어서 directory1 길이로 통일하기로 했다.
    # 空白文字を使うことで見た目を整えることができる!
    directory\tfile2    \t 
    file1    \tfile3    \t
    
    문자열 길이의 통일도 directory1 방법을 통해 실현할 수 있다.
    약간printf을 변경하여 너비를 지정할 수 있습니다.
    너비 지정
    printf("%15s", files[0])
    # => "     directory1" 15文字になるよう空白文字が追加される。
    
    printf("%-15s", files[0])
    # => "directory1     " -をつけると左詰めになる。
    
    name_len = 15
    
    printf("%-#{name_len}s\t", files[0]) # => 式展開もできる。
    # => "directory1     "
    
    %s 너비를 지정하면
    $ ruby ls.rb test
    directory1      directory3      directory6      directory9      file2           file5           file8
    directory10     directory4      directory7      file1           file3           file6           file9
    directory2      directory5      directory8      file10          file4           file7
    
    ls.rb 동일한 출력을 얻었습니다!

    해설


    전체 해설을 하면 길어지기 때문에 출력 형식 중 설명하는 곳만 해설한다.
    또 호출 방법에 대한 상세한 설명 없이 평론문으로만 기능에 대한 해설을 한다.
    소스 코드는 완성 코드 에 있습니다.
    전체적인 설치에 신경을 쓰는 사람은 이곳을 참조하세요.

    self.display_normal

      def self.display_normal(dir) 
        files = get_files(dir) # ディレクトリ内のファイル名の配列を、filesに代入する。
    
        name_len = 1 # ファイル名の最大値を格納するname_lenを用意する。
        files.map { |file| name_len = file.length if name_len < file.length } # 一番長いファイル名を、name_lenに代入する。
    
        total_length = (name_len + 5) * files.count # 出力すべき文字数を算出。
        columns = `tput cols`.to_i # ターミナルの1行の文字数を取得。
    
        line_count = (total_length + (columns - 1)) / columns # 必要な出力行数を求める。
        column_count = (files.count + (line_count / 2)) / line_count # 1行に表示できるファイル数を求める。
        line_count = 1 if line_count == 0 # 最低でも1行は出力する必要があるため、line_countが0になっていた場合、1を代入する。
    
        (0...line_count).each do |line| 
          (0..column_count).each do |column| 
            idx = line_count * column + line 
            printf("%-#{name_len}s\t", files[idx]) if idx < files_count # 添字が配列内の大きさを超えていなければ、ターミナルに出力する
          end
          print("\n") # ここに到達すると1行分の出力が終わっているので改行する。
        end
      end
    

    운행 시간

    ls 명령을 사용하여 실행 시간을 측정합니다.
    $ time ls test                                                                    
    directory1  directory3  directory6  directory9  file2       file5       file8
    directory10 directory4  directory7  file1       file3       file6       file9
    directory2  directory5  directory8  file10      file4       file7
    ls test  0.00s user 0.00s system 78% cpu 0.008 total
    
    #  ls testの実行時間 0.00s
    
    $  time ruby ls.rb test                                                            
    directory1  directory3  directory6  directory9  file2       file5       file8
    directory10 directory4  directory7  file1       file3       file6       file9
    directory2  directory5  directory8  file10      file4       file7
    ruby ls.rb test  0.10s user 0.07s system 86% cpu 0.191 total 
    
    # ruby ls.rbの実行時間 0.10s
    
    눈으로 보면 time 보다 더 느리다.

    느끼다


    실용성은 없지만 ls 명령을 재현할 수 있다고 생각합니다.
    바퀴의 재개발은 졸렬한 코드라고 하지만 주력하기 전보다 설치력이 높아졌다고 생각한다.
    만약 날짜가 충분하다면, 나는 테스트를 써서 기능을 확장하고 싶다.
    또 항상 한 손으로 문서를 들고 일하기 때문에 문서를 읽는 능력도 습득했다.
    본 기사에서는 설명할 수 없지만 ls 설치도 많이 배웠다.

    끝내다


    이 광고 달력은 웹과 기사가 많아서 조금 바뀐 것을 기사로 삼고 싶습니다.
    다른 사람에 비해 직접 공부하는 경우는 드물겠지만 조금만 참고하면 좋을 것 같아요.
    내일이 드디어 마지막 날이다.
    @hide9138의 보도는 본 행사 달력이 좋은 결말을 맞이하길 바랍니다.
    그럼 메리 크리스마스

    완성 코드


    Github : https://github.com/shinooooo/Ruby-sh

    참고 자료


    Ruby 2.7.0 참조 안내서
    Man page of LS

    좋은 웹페이지 즐겨찾기