Uso practico de bash: Ejecutar un comando sobre varios archivos
Alex Delgado
Posted on July 6, 2022
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 considerablemente, 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 parámetros 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 operador pipe, que permite ingresar la salida de un comando como la entrada de un comando posterior, pasando asi nuestra lista de archivos al comando xargs
$ $ 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 representar 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
Posted on July 6, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024
November 27, 2024