Uso practico de bash: Ejecutar un comando sobre varios archivos

10616 단어 spanish
Existen ocasiones en los que es necesario aplicar una serie de comandos en diferentes archivos, lo cual seria muy tedioso realizar manualmente, pero se puede lograr fácilmente con un poco de scripting en bash.

Vamos a empezar dando un ejemplo practico, tenemos un proyecto con la siguiente estructura:

.
├── Dockerfile
├── LICENSE
├── MANIFEST.in
├── README.md
├── VERSION
├── flake8.ini
├── mypy.ini
├── requirements_dev.txt
├── requirements_pkg.txt
├── setup.py
├── src
│   └── bodywork
│       ├── __init__.py
│       ├── cli
│       │   ├── __init__.py
│       │   ├── cli.py
│       │   ├── deployments.py
│       │   ├── secrets.py
│       │   ├── setup_namespace.py
│       │   ├── terminal.py
│       │   └── workflow_jobs.py
│       ├── config.py
│       ├── constants.py
│       ├── exceptions.py
│       ├── git.py
│       ├── k8s
│       │   ├── __init__.py
│       │   ├── auth.py
│       │   ├── batch_jobs.py
│       │   ├── deployments.py
│       │   ├── namespaces.py
│       │   ├── pod_logs.py
│       │   ├── secrets.py
│       │   ├── utils.py
│       │   └── workflow_jobs.py
│       ├── logs.py
│       ├── stage_execution.py
│       └── workflow_execution.py
├── structure.txt
├── tests
│   ├── conftest.py
│   ├── integration
│   │   ├── conftest.py
│   │   ├── test_git_integration.py
│   │   ├── test_k8s_with_cluster.py
│   │   └── test_k8s_with_secrets.py
│   ├── resources
│   │   └── project_repo
│   │       ├── bodywork.ini
│   │       ├── bodywork.yaml
│   │       ├── bodywork_bad_stages_section.yaml
│   │       ├── bodywork_batch_stage.yaml
│   │       ├── bodywork_empty.yaml
│   │       ├── bodywork_missing_sections.yaml
│   │       ├── on_fail_stage
│   │       │   └── main.py
│   │       ├── stage_1
│   │       │   └── main.py
│   │       ├── stage_2
│   │       │   └── main.py
│   │       ├── stage_3
│   │       │   └── main.py
│   │       ├── stage_4
│   │       │   └── main.py
│   │       ├── stage_5
│   │       │   └── main.py
│   │       └── stage_jupyter
│   │           └── main.ipynb
│   └── unit_and_functional
│       ├── conftest.py
│       ├── test_cli.py
│       ├── test_cli_deployments.py
│       ├── test_cli_secrets.py
│       ├── test_cli_setup_namespace.py
│       ├── test_cli_terminal.py
│       ├── test_cli_workflow_jobs.py
│       ├── test_config.py
│       ├── test_git.py
│       ├── test_k8s_auth.py
│       ├── test_k8s_batch_jobs.py
│       ├── test_k8s_deployments.py
│       ├── test_k8s_namespaces.py
│       ├── test_k8s_pod_logs.py
│       ├── test_k8s_secrets.py
│       ├── test_k8s_utils.py
│       ├── test_k8s_workflow_jobs.py
│       ├── test_logs.py
│       ├── test_stage_execution.py
│       └── test_workflow_execution.py
├── tox.ini
└── xclip

16 directories, 75 files


Nuestra tarea es cambiar todos los Docstrings dentro de los archivos python, a Formato Google , esto podría llevarnos horas y es propenso a errores, ya que es probable que ni siquiera conozcamos todas las diferencias entre ambos formatos.

Para ello después de un poco de Investigación, encontramos en github el paquete Pyment, el cual resuelve el problema de tener que modificar los archivos de manera manual, sin embargo, si leemos en la documentación, podemos encontrar que para realizar esta tarea se requieren una serie de comandos sobre cada archivo:

# Generar el archivo patch
$ pyment test.py

# Aplicar patch al archivo original
$ patch -p1 test.py.patch

# Borrar archivo patch
$ rm test.py.patch


Si bien la cantidad de trabajo disminuye 상당한mente, todavía es necesario encontrar cada archivo y ejecutar estos comandos, lo cual sigue consumiendo bastante tiempo ( sin mencionar lo tedioso que puede ser)

Para hacer esto aun mas sencillo, usaremos el siguiente comando:

$ find . -name '*.py' | xargs  -I % bash -c 'cd $(dirname %) ; pyment $(basename %); patch -p1 < $(basename %).patc


Dividamos este comando en varias para explicarlo mas fácilmente, Primero que nada:

$ find . -name '*.py'
./setup.py
./src/bodywork/workflow_execution.py
./src/bodywork/__init__.py
./src/bodywork/config.py
./src/bodywork/logs.py
./src/bodywork/exceptions.py
./src/bodywork/constants.py
./src/bodywork/k8s/utils.py
./src/bodywork/k8s/pod_logs.py
./src/bodywork/k8s/deployments.py
./src/bodywork/k8s/__init__.py
./src/bodywork/k8s/batch_jobs.py
./src/bodywork/k8s/workflow_jobs.py
./src/bodywork/k8s/namespaces.py
./src/bodywork/k8s/secrets.py
./src/bodywork/k8s/auth.py
./src/bodywork/cli/cli.py
./src/bodywork/cli/deployments.py
./src/bodywork/cli/__init__.py
./src/bodywork/cli/setup_namespace.py
./src/bodywork/cli/terminal.py
./src/bodywork/cli/workflow_jobs.py
./src/bodywork/cli/secrets.py
./src/bodywork/stage_execution.py
./src/bodywork/git.py
./tests/unit_and_functional/test_k8s_batch_jobs.py
./tests/unit_and_functional/test_k8s_auth.py
./tests/unit_and_functional/test_cli_secrets.py
./tests/unit_and_functional/test_k8s_utils.py
./tests/unit_and_functional/test_cli_workflow_jobs.py
./tests/unit_and_functional/test_cli_setup_namespace.py
./tests/unit_and_functional/test_k8s_deployments.py
./tests/unit_and_functional/test_k8s_namespaces.py
./tests/unit_and_functional/test_k8s_pod_logs.py
./tests/unit_and_functional/test_cli_deployments.py
./tests/unit_and_functional/test_workflow_execution.py
./tests/unit_and_functional/test_cli_terminal.py
./tests/unit_and_functional/test_stage_execution.py
./tests/unit_and_functional/test_k8s_secrets.py
./tests/unit_and_functional/test_k8s_workflow_jobs.py
./tests/unit_and_functional/conftest.py
./tests/unit_and_functional/test_logs.py
./tests/unit_and_functional/test_config.py
./tests/unit_and_functional/test_cli.py
./tests/unit_and_functional/test_git.py
./tests/resources/project_repo/stage_3/main.py
./tests/resources/project_repo/stage_2/main.py
./tests/resources/project_repo/stage_4/main.py
./tests/resources/project_repo/stage_1/main.py
./tests/resources/project_repo/on_fail_stage/main.py
./tests/resources/project_repo/stage_5/main.py
./tests/integration/test_git_integration.py
./tests/integration/test_k8s_with_cluster.py
./tests/integration/conftest.py
./tests/integration/test_k8s_with_secrets.py
./tests/conftest.py


  • El comando find regresa una lista de archivos que cumplan con los parametros de búsqueda especificado
  • Este comando require especificar el directorio a partir del cual se buscaran archivos, utilizamos . que es un alias para el directorio actual
  • La bandera -name '*.py' indica nuestro parámetro de búsqueda, que en este caso seran todos los archivos con terminación .py

  • Utilizaremos el operator pipe, que permite ingresar la salida de un comando como la entrada de un comando posterior, pasando asi nuestra lista de archivos al comandoxargs
    $ $ xargs  -I % bash -c 'cd $(dirname %) ; pyment $(basename %); patch -p1 < $(basename %).patc
    


  • El comando xargs nos permite ejecutar un comando a cada uno de los elementos de una lista, en este caso, nos permite ejecutar un comando a cada uno de los archivos resultantes del comando anterior
  • La bandera-I % Nos permite especificar un símbolo que sera utilizado como placeholder dentro del comando para Representativear el nombre del archivo.
  • Los argumentos siguientes bash -c 'cd $(dirname %) ; pyment $(basename %); patch -p1 < $(basename %).patch; rm $(basename %).patch especifican el comando que sera utilizado en cada iteración:

  • # Cambiar al directorio del archivo
    # Recordemos que el comando find nos devuele resultados de la siguiente manera
    # ./src/bodywork/workflow_execution.py
    # El comando dirname nos regresa el directorio de un archivo, removiendo
    # el placeholder esto sera equivalente a 
    # cd $(dirname ./src/bodywork/workflow_execution.py);
    # cd ./src/bodywork/
    $ cd $(dirname %);
    
    # Una vez dentro del directorio ejecutamos el comando pyment sobre el archivo
    # De igual manera, el comando basename regresa el nombre de un archivo,
    # Sustituyendo el placeholder esto sera equivalente a
    # pyment $(basename ./src/bodywork/workflow_execution.py);
    # pyment workflow_execution.py
    $ pyment $(basename %);
    
    # Aplicar patch
    # Sustituyendo el placeholder esto sera equivalente a
    # $ patch -p1 < $(basename /src/bodywork/workflow_execution.py).patch'
    # $ patch -p1 < workflow_execution.py.patch
    $ patch -p1 < $(basename %).patch'
    
    # Eliminar Archivo patch generado
    $ rm $(basename %).patch
    
    

    좋은 웹페이지 즐겨찾기