파이톤으로 조개 스크립트를 다시 썼어요.

56580 단어 PythonBashshelltech

개요


나는 일을 더욱 수월하게 하기 위해 개인적으로 조개 각본을 썼다.
나는 이것을 다른 구성원들과 공유하여 팀 전체의 업무 효율을 향상시키고 개선하고 싶다.
하지만 조개 스크립트를 접한 멤버가 적어 접한 파이톤으로 다시 쓰기로 했다.(무슨 시험 문제의 도입문인 것 같다)

어떤 조개 스크립트일까요?


케이스 스크립트는 여기 기사.의 템플릿을 참조했습니다.
처리 내용은 대략 다섯 개다.
  • 템플릿 파일 복사, 파일 내용 부분 교체
  • 오류 파일 이름 시 대량 교체
  • 컴파일된 임시 파일 삭제
  • 편집기에서 TeX 파일 열기
  • PDF 관찰기로 출력 PDF 파일 열기
  • 그러나 구체적인 처리가 중요한 것이 아니어서 접어서 썼다.↓
    조개 스크립트는 대충 이런 느낌
    #!/usr/bin/env bash
    set -Eeuo pipefail
    trap cleanup SIGINT SIGTERM ERR EXIT
    
    script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P)
    
    usage() {
      cat <<EOF
    Usage:
    bash $(basename "${BASH_SOURCE[0]}") [-h] [-v] [-ot] [-op] [-r aftername] dirname
    
    # これは何?
    hoge用のシェルスクリプトです。
    dirnameに作業ディレクトリ名を入力してください。
    ディレクトリ構成は以下です。
    -------------------------------
    ./
    ├── cli.sh
    └── template
        ├── A.tex
        ├── QandA.tex
        └── Q.tex
    -------------------------------
    
    # 使い方
    "bash cli.sh hoge"とコマンドラインに入力することで、
    カレントディレクトリにhogeという作業ディレクトリが作成されます。
    構成は以下です。
    -------------------------------
    hoge
    ├── hoge_Q.tex ... 問題texファイル
    ├── hoge_A.tex ... 解答texファイル
    └── hoge.tex ... 問題と解答を1つにするtexファイル
    -------------------------------
    
    また、"bash cli.sh fuga/hoge"とコマンドラインに入力することで、
    カレントディレクトリにfugaというディレクトリが作成され、
    fugaの中にhogeという作業ディレクトリが作成されます。
    構成は以下です。
    -------------------------------
    hoge
    ├── hoge_Q.tex ... texファイル1
    ├── hoge_A.tex ... texファイル2
    └── hoge.tex ... 上記2つを1つにするtexファイル
    -------------------------------
    
    
    # オプション一覧
    -h, --help      ヘルプを表示
    -v, --verbose   デバッグ用なので気にしないで
    -ot, --opentex  エディタでTeXファイルを開く
    -op, --openpdf  出力PDFファイルを開く
    -c, --clear     コンパイルで作られた一時ファイルを削除
    -r, --rename    間違えたとき用に一括置換
    EOF
      exit
    }
    
    cleanup() {
      trap - SIGINT SIGTERM ERR EXIT
    }
    
    setup_colors() {
      if [[ -t 2 ]] && [[ -z "${NO_COLOR-}" ]] && [[ "${TERM-}" != "dumb" ]]; then
        NOFORMAT='\033[0m' RED='\033[0;31m' GREEN='\033[0;32m' ORANGE='\033[0;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' YELLOW='\033[1;33m'
      else
        NOFORMAT='' RED='' GREEN='' ORANGE='' BLUE='' PURPLE='' CYAN='' YELLOW=''
      fi
    }
    
    msg() {
      echo >&2 -e "${1-}"
    }
    
    die() {
      local msg=$1
      local code=${2-1}
      msg "$msg"
      exit "$code"
    }
    
    parse_params() {
      opentex=0
      openpdf=0
      rename=''
      clearfiles=0
    
      while :; do
        case "${1-}" in
        -h | --help) usage ;;
        -v | --verbose) set -x ;;
        --no-color) NO_COLOR=1 ;;
        -ot | --opentex) opentex=1 ;;
        -op | --openpdf) openpdf=1 ;;
        -c | --clear) clearfiles=1 ;;
        -r | --rename)
          rename="${2-}"
          shift
          ;;
        -?*) die "Unknown option: $1" ;;
        *) break ;;
        esac
        shift
      done
    
      args=("$@")
    
      [[ ${#args[@]} -eq 0 ]] && die "作業ディレクトリを指定してください。"
    
      return 0
    }
    
    parse_params "$@"
    setup_colors
    
    # ----------------------------------------------------------------
    dirname=${args[0]}
    filename=${dirname##*/}
    if [[ -n $rename ]]; then
      if [[ ! -d $dirname ]]; then
        die "「$dirname」が存在しません。"
      fi
      echo -n "$dirnameから$renameへ一括置換をしますか? (y/n) >"
      read do_rename
      if [[ $do_rename = 'y' ]]; then
        sed -i s/$dirname/$rename/g $dirname/$filename.tex
        mkdir $rename
        mv $dirname/$filename.tex $rename/$rename.tex
        mv $dirname/${filename}_Q.tex $rename/${rename}_Q.tex
        mv $dirname/${filename}_A.tex $rename/${rename}_A.tex
        rmdir $dirname
        echo "$dirnameから$renameへ一括置換しました"
      else
        echo "一括置換は行われませんでした"
      fi
      exit 0
    fi
    
    if [[ ! -d $dirname ]]; then
      mkdir $dirname
      cp template/QandA.tex $dirname/$filename.tex
      cp template/Q.tex $dirname/${filename}_Q.tex
      cp template/A.tex $dirname/${filename}_A.tex
      sed -i s/DIRNAME/$filename/g $dirname/$filename.tex
    fi
    
    pdf=$dirname/$filename.pdf
    if [[ $openpdf = 1 ]]; then
      if [[ -f $pdf ]]; then
        /mnt/c/Program\ Files/SumatraPDF/SumatraPDF.exe $pdf &
      else
        /mnt/c/Program\ Files/SumatraPDF/SumatraPDF.exe &
      fi
    fi
    
    if [[ $clearfiles = 1 ]]; then
      rm $dirname/*.aux $dirname/*.dvi $dirname/*.fdb_latexmk $dirname/*.fls $dirname/*.log $dirname/*.synctex.gz
    fi
    
    if [[ $opentex = 1 ]]; then
      cd $dirname
      vim *.tex
    fi
    
    내 환경은 Ubuntu(WSL2)이기 때문에 텍스트 편집기와 PDF의 시청자 부분은 각자 다시 쓰십시오.이외의 섹션 = 파일 작업은 Windows와 Ubuntu를 막론하고 향상됩니다.또한 파이썬은 표준 라이브러리만 사용합니다.

    고쳐 쓰다


    매개변수 및 도움말


    조개 스크립트는 이렇게 매개 변수를 처리한다while.
    조개각본
    opentex=0
    openpdf=0
    rename=''
    clearfiles=0
    
    while :; do
      case "${1-}" in
      -h | --help) usage ;;
      -v | --verbose) set -x ;;
      --no-color) NO_COLOR=1 ;;
      -ot | --opentex) opentex=1 ;;
      -op | --openpdf) openpdf=1 ;;
      -c | --clear) clearfiles=1 ;;
      -r | --rename)
        rename="${2-}"
        shift
        ;;
      -?*) die "Unknown option: $1" ;;
      *) break ;;
      esac
      shift
    done
    
    args=("$@")
    
    [[ ${#args[@]} -eq 0 ]] && die "作業ディレクトリを指定してください。"
    
    또한 도움말의 내용은 usage이라는 함수에 쓰여 있다.
    조개각본
    usage() {
      cat <<EOF
    Usage:
    bash $(basename "${BASH_SOURCE[0]}") [-h] [-v] [-ot] [-op] [-r aftername] dirname
    
    # これは何?
    省略
    
    # 詳しい使い方
    省略
    
    # オプション一覧
    -h, --help      ヘルプを表示
    省略
    EOF
      exit
    }
    
    Python으로 이것을 다시 쓰려면 argparse 모듈 을 사용하십시오.
    Python
    import argparse
    p = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description="""
    
    # これは何?
    省略
    
    # 詳しい使い方
    省略
    """,
    )
    # 位置引数
    p.add_argument("dirname", help="作業ディレクトリ名")
    # オプション引数
    group = p.add_mutually_exclusive_group()
    group.add_argument("-ot", "--opentex", help="エディタでTeXファイルを開く", action="store_true")
    group.add_argument("-op", "--openpdf", help="出力PDFファイルを開く", action="store_true")
    group.add_argument("-c", "--clear", help="コンパイルで作られた一時ファイルを削除", action="store_true")
    group.add_argument("-r", "--rename", help="間違えたとき用に一括置換")
    args = p.parse_args()
    
    ArgumentParser 대상을 만들고 add_argument 방법으로 파라미터를 추가합니다.매개 변수의 값은 parse_args에서 args.dirname, args.opentex 등으로 되돌아갈 수 있다.
    자기가 --help 옵션을 추가하지 않아도 add_argument 방법help으로 usage와 옵션을 잘 표시할 수 있습니다.
    usage: cli.py [-h] [-ot | -op | -c | -r RENAME] dirname
    
    positional arguments:
      dirname               作業ディレクトリ名
    
    optional arguments:
      -h, --help            show this help message and exit
      -ot, --opentex        エディタでTeXファイルを開く
      -op, --openpdf        出力PDFファイルを開く
      -c, --clear           コンパイルで作られた一時ファイルを削除
      -r RENAME, --rename RENAME
                            間違えたとき用に一括置換
    
    이번에는 description도 지정했다.도움말의 usage와positional arguments 사이에 description 지정한 내용을 추가합니다.description에서 줄을 원상태로 바꾸기 위해formatter_class에서 RawDescriptionHelpFormatter를 지정했습니다.add_argument 방법에서 머리에 -를 첨가한 후 선택할 수 있는 매개 변수이고 첨가하지 않으면 위치 매개 변수이다.이 설정action="store_true"에서 매개변수가 지정되면 True를 사용할 수 있고 그렇지 않으면 False를 사용할 수 있습니다.ArgumentParser의 대상에서 add_mutually_exclusive_group 방법을 사용하면 조합 내의 매개 변수를 2개 이상 동시에 사용할 수 없다.usage에도 그걸 반영했네요.
    그리고
    if args.opentex:
        pass
    
    처럼 조건에 따라 띄어쓰기만 하면 된다.

    파일 작업


    Unix든 Windows든 os 모듈을 사용하여 처리 경로를 적절히 만들 수 있습니다.
    바쁜 사람을 위해 자주 쓰는 것들을 나열하다.
    os.sep: 경로 구분자 가져오기
    os.path.join: 경로의 결합
    os.path.split: 패스의 끝 및 이전 분할
    os.path.exists: 경로가 실제로 존재하는지 여부
    os.rename: 이름 바꾸기
    os.mkdir: 디렉토리 만들기
    os.remove: 삭제
    기억하기 쉬운데.
    서류의 복사본은 shutil.copyfile(src, dist)처럼 shutil 모듈을 사용했다(이것만 갑자기 머릿속에 나타나지 않았다).

    편집기 시작 등


    subprocess 모듈을 사용하여 텍스트 편집기와 PDF 관찰기를 시작합니다.
    Python
    import subprocess as sb
    if args.opentex:
        sb.run("cd {} && vim *.tex".format(dirname), shell=True)
    
    원래 첫 번째 파라미터에 명령과 파라미터의 목록을 지정하였으나 문자열로 직접 쓰려고 지정하였습니다shell=True.출력을 변경할 때는 stdout 또는 stderr로 지정할 수 있습니다.

    완성품


    그래서 완제품은 여기에 있다
    import argparse
    import os
    import re
    import sys
    import shutil
    import subprocess as sb
    
    p = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description="""
    # これは何?
    dirnameに作業ディレクトリ名を入力してください。
    ディレクトリ構成は以下です。
    -------------------------------
    ./
    ├── cli.py
    └── template
        ├── A.tex
        ├── QandA.tex
        └── Q.tex
    -------------------------------
    
    
    # 使い方
    ## 主な使い方
    "python cli.py hoge"とコマンドラインに入力することで、
    カレントディレクトリにhogeという作業ディレクトリが作成されます。
    構成は以下です。
    -------------------------------
    hoge
    ├── hoge_Q.tex ... 問題texファイル
    ├── hoge_A.tex ... 解答texファイル
    └── hoge.tex ... 問題と解答を1つにするtexファイル
    -------------------------------
    
    また、"python cli.py fuga/hoge"とコマンドラインに入力することで、
    カレントディレクトリにfugaというディレクトリが作成され、
    fugaの中にhogeという作業ディレクトリが作成されます。
    構成は以下です。
    -------------------------------
    hoge
    ├── hoge_Q.tex ... 問題texファイル
    ├── hoge_A.tex ... 解答texファイル
    └── hoge.tex ... 問題と解答を1つにするtexファイル
    -------------------------------
    
    以下省略
    """,
    )
    # 位置引数
    p.add_argument("dirname", help="作業ディレクトリ名")
    # オプション引数
    group = p.add_mutually_exclusive_group()
    group.add_argument("-ot", "--opentex", help="エディタでTeXファイルを開く", action="store_true")
    group.add_argument("-op", "--openpdf", help="出力PDFファイルを開く", action="store_true")
    group.add_argument("-c", "--clear", help="コンパイルで作られた一時ファイルを削除", action="store_true")
    group.add_argument("-r", "--rename", help="間違えたとき用に一括置換")
    args = p.parse_args()
    
    # hoge でも hoge/fuga でも hoge/ でも hoge/fuga/ でも対応 windowsでも対応
    dirname = re.sub(repr(os.sep + "$")[1:-1], "", args.dirname)
    filename = os.path.split(dirname)[-1]
    dir_file_name = os.path.join(dirname, filename)
    
    # 一括置換
    if args.rename:
        re_dirname = re.sub(repr(os.sep + "$")[1:-1], "", args.rename)
        re_filename = os.path.split(re_dirname)[-1]
        # ファイルチェック
        if not os.path.exists(dirname):
            print("「{}」が存在しません".format(dirname), file=sys.stderr)
            sys.exit(1)
        if os.path.exists(re_dirname):
            print("「{}」は既に存在しています".format(re_dirname), file=sys.stderr)
            sys.exit(1)
    
        do_rename = input("{}から{}へ一括置換をしますか? (y/n) >".format(dirname, re_dirname))
        if do_rename == "y":
            # ファイル内の文字列を置換
            with open(dir_file_name + ".tex") as f:
                lines = f.read()
            lines = lines.replace(filename, re_filename)
            with open(dir_file_name + ".tex", mode="w") as f:
                f.write(lines)
    
            # ファイル名のリネーム
            os.rename(dirname, re_dirname)
            for qa in ["", "_Q", "_A"]:
                os.rename(
                    os.path.join(re_dirname, filename + qa + ".tex"),
                    os.path.join(re_dirname, re_filename + qa + ".tex"),
                )
    
            print("{}から{}へ一括置換しました".format(dirname, re_dirname))
            sys.exit(0)
        else:
            print("一括置換は行われませんでした")
            sys.exit(0)
    
    # テンプレートのコピー
    if not os.path.exists(dirname):
        os.mkdir(dirname)
    
        # テンプレ文字列を置換
        with open(os.path.join("template", "QandA.tex")) as f:
            lines = f.read()
        lines = lines.replace("DIRNAME", filename)
    
        # テンプレをコピー
        with open(dir_file_name + ".tex", mode="w") as f:
            f.write(lines)
        shutil.copyfile(os.path.join("template", "Q.tex"), dir_file_name + "_Q.tex")
        shutil.copyfile(os.path.join("template", "A.tex"), dir_file_name + "_A.tex")
    
    if args.openpdf:
        pdf = os.path.join(dirname, filename + ".pdf")
        if os.path.exists(pdf):
            sb.run(
                "/mnt/c/Program\ Files/SumatraPDF/SumatraPDF.exe {} &".format(pdf),
                stdout=sb.DEVNULL,
                stderr=sb.DEVNULL,
                shell=True,
            )
        else:
            sb.run(
                "/mnt/c/Program\ Files/SumatraPDF/SumatraPDF.exe & ",
                shell=True,
            )
    
    if args.clear:
        for rf in [".aux", ".dvi", ".fdb_latexmk", ".fls", ".log", ".synctex.gz"]:
            os.remove(dir_file_name + rf)
    
    if args.opentex:
        sb.run("cd {} && vim *.tex".format(dirname), shell=True)
    
    파이톤에 익숙해서 쓰기가 편해요.매개 변수의 해석과 도움말을 함께 쓰기 때문에 usage와 옵션의 표시도 마음대로 쉽게 할 수 있습니다.argparse에도 자습서가 있다.

    좋은 웹페이지 즐겨찾기