Cómo comprobar la presencia de archivos y carpetas en macOS de forma automatizada

En el mundo informático, la automatización de tareas repetitivas puede ahorrar tiempo y reducir errores. Una de estas tareas consiste en comprobar la presencia de archivos o carpetas específicos en varios directorios. Ya sea para el cumplimiento de normativas, la supervisión del sistema o la solución de problemas, disponer de una forma automática de comprobar la presencia de archivos puede ser muy valioso para los profesionales de TI y los proveedores de servicios gestionados (MSP). Este post explora un script Bash diseñado para agilizar este proceso, garantizando la eficiencia y la fiabilidad en la gestión de archivos.

Background

Este script para comprobar la presencia de archivos es especialmente útil para los profesionales de TI que necesitan verificar la existencia de archivos o carpetas críticos de forma regular. Proporciona una solución automatizada para buscar en directorios, asegurándose de comprobar la presencia de archivos importantes o identificando cuándo faltan. Esta capacidad es crucial en varios escenarios, como validar las ubicaciones de las copias de seguridad, garantizar la presencia de archivos de configuración o confirmar el despliegue de aplicaciones críticas.

El script para comprobar la presencia de archivos

#!/usr/bin/env bash

# Description: Alert if a specified file or folder is found in a directory or subdirectory you specify.
#
# Release Notes: Initial Release
# By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use.
# Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. 
# Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. 
# Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. 
# Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. 
# Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. 
# Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. 
# EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).
#
# Below are all the (case sensitive) valid parameters for this script.
# Only the path to search and name of file or folder are required!
#
# Parameter: --path "/opt/NinjaRMM/programdata"
#   Required
#   Base path to search for files or folders.
#
# Parameter: --name "ninjarmm-cli"
#   Required
#   Name of the file or folder to search for.
#   Notes:
#       If the name is not provided, the script will search for the path only.
#       This is case sensitive and accepts wildcards.
#
# Parameter: --type "Files Or Folders"
#   Required
#   Search for files or folders.
#
# Parameter: --type "Files Only"
#   Required
#   Searches for files only.
#
# Parameter: --type "Folders Only"
#   Required
#   Searches for folder only.
#
# Parameter: --timeout 10
#   Optional and defaults to 30 minutes
#   Time in minutes to wait for the search to complete before timing out.
#
# Parameter: --customfield "myCustomField"
#   Optional
#   Custom Field to save the search results to.

die() {
    local _ret="${2:-1}"
    test "${_PRINT_HELP:-no}" = yes && print_help >&2
    echo "$1" >&2
    exit "${_ret}"
}

begins_with_short_option() {
    local first_option all_short_options='h'
    first_option="${1:0:1}"
    test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
}

# Initize arguments
_arg_path=
_arg_name=
_arg_type=
_arg_timeout=30
_arg_customfield=

print_help() {
    printf '%s\n' "Check existence of a file or folder"
    printf 'Usage: %s [--path <arg>] [--name <arg>] [--type <"Files Only"|"Folders Only"|"Files Or Folders">] [--timeout <30>] [--customfield <arg>] [-h|--help]\n' "$0"
    printf '\t%s\n' "-h, --help: Prints help"
}

parse_commandline() {
    while test $# -gt 0; do
        _key="$1"
        case "$_key" in
        --path)
            test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
            _arg_path="$2"
            shift
            ;;
        --path=*)
            _arg_path="${_key##--path=}"
            ;;
        --name)
            test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
            _arg_name="$2"
            shift
            ;;
        --name=*)
            _arg_name="${_key##--name=}"
            ;;
        --type)
            test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
            _arg_type="$2"
            shift
            ;;
        --type=*)
            _arg_type="${_key##--type=}"
            ;;
        --timeout)
            test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
            _arg_timeout="$2"
            shift
            ;;
        --timeout=*)
            _arg_timeout="${_key##--timeout=}"
            ;;
        --customfield)
            test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
            _arg_customfield="$2"
            shift
            ;;
        --customfield=*)
            _arg_customfield="${_key##--customfield=}"
            ;;
        -h | --help)
            print_help
            exit 0
            ;;
        -h*)
            print_help
            exit 0
            ;;
        *)
            _PRINT_HELP=yes die "FATAL ERROR: Got an unexpected argument '$1'" 1
            ;;
        esac
        shift
    done
}

parse_commandline "$@"

function SetCustomField() {
    customfieldName=$1
    customfieldValue=$2
    if [ -f "${NINJA_DATA_PATH}/ninjarmm-cli" ]; then
        if [ -x "${NINJA_DATA_PATH}/ninjarmm-cli" ]; then
            if "$NINJA_DATA_PATH"/ninjarmm-cli get "$customfieldName" >/dev/null; then
                # check if the value is greater than 10000 characters
                if [ ${#customfieldValue} -gt 10000 ]; then
                    echo "[Warn] Custom field value is greater than 10000 characters"
                fi
                if ! echo "${customfieldValue::10000}" | "$NINJA_DATA_PATH"/ninjarmm-cli set --stdin "$customfieldName"; then
                    echo "[Warn] Failed to set custom field"
                else
                    echo "[Info] Custom field value set successfully"
                fi
            else
                echo "[Warn] Custom Field ($customfieldName) does not exist or agent does not have permission to access it"
            fi
        else
            echo "[Warn] ninjarmm-cli is not executable"
        fi
    else
        echo "[Warn] ninjarmm-cli does not exist"
    fi
}

if [ ! "$(command -v timeout)" ]; then
    notimeout=true
    # If the timeout command does not exist, create a function to mimic the timeout command
    function timeout() { perl -e 'alarm shift; exec @ARGV' "$@"; }
fi

parentSearchPath=$_arg_path
leafSearchName=$_arg_name
searchType=$_arg_type
timeout=$_arg_timeout
customField=$_arg_customfield

# Get values from Script Variables
if [[ -n "${pathToSearch}" ]]; then
    parentSearchPath="${pathToSearch}"
fi
if [[ -n "${nameOfFileOrFolder}" ]]; then
    leafSearchName="${nameOfFileOrFolder}"
fi
if [[ -n "${filesOrFolders}" && "${filesOrFolders}" != "null" ]]; then
    searchType="${filesOrFolders}"
fi
if [[ -n "${searchTimeout}" && "${searchTimeout}" != "null" ]]; then
    timeout="${searchTimeout}"
fi
if [[ -n "${customFieldName}" && "${customFieldName}" != "null" ]]; then
    customField="${customFieldName}"
fi

# Check if parentSearchPath is a link and replace it with the resolved path
if [ -L "${parentSearchPath}" ]; then
    echo "[Info] Path to Search is a link: ${parentSearchPath} -> $(readlink -f "${parentSearchPath}")"
    echo "[Info] Will use the resolved path to search"
    parentSearchPath=$(readlink -f "${parentSearchPath}")
fi

if [[ -z "${parentSearchPath}" ]]; then
    echo "[Error] Path to Search is empty"
    exit 1
fi

# Check if path exists
if [ -e "${parentSearchPath}" ]; then
    echo "[Info] Path ${parentSearchPath} exists"
else
    echo "[Error] Path to Search ${parentSearchPath} does not exist or is an invalid path"
    exit 1
fi

# Check if timeout is a number
if ! [[ "${timeout}" =~ ^[0-9]+$ ]]; then
    echo "[Error] Timeout is not a number"
    exit 1
fi
# Check if timeout is not in the range of 1 to 120
if [[ "${timeout}" -lt 1 || "${timeout}" -gt 120 ]]; then
    echo "[Error] Timeout is not in the range of 1 to 120"
    exit 1
fi

# Check if search type is valid

if $notimeout; then
    # If the timeout command does not exist, convert the timeout to minutes
    timeout=$((timeout * 60))
else
    # If the timeout command does exist, add m to the end of the string
    timeout="${timeout}m"
fi

if [[ $OSTYPE == 'darwin'* ]]; then
    if ! plutil -lint /Library/Preferences/com.apple.TimeMachine.plist >/dev/null; then
        echo "This script requires ninjarmm-macagent to have Full Disk Access."
        echo "Add ninjarmm-macagent to the Full Disk Access list in System Preferences > Security & Privacy, quit the app, and re-run this script."
        exit 1
    fi
fi

# Search for files or folders
if [[ -n "${leafSearchName}" && "${leafSearchName}" != "null" ]]; then
    if [[ "${searchType}" == *"Files"* && "${searchType}" == *"Only"* ]]; then
        echo "[Info] Searching for files only"
        # Search for files only
        # Use timeout to prevent the find command from running indefinitely
        foundPath=$(timeout "${timeout}" find "$parentSearchPath" -type f -name "$leafSearchName" 2>/dev/null)
        exitcode=$?
        if [[ $exitcode -eq 0 || $exitcode -eq 124 ]]; then
            if [[ -n $foundPath ]]; then
                echo "[Alert] File Found"
            fi
        fi
    elif [[ "${searchType}" == *"Folders"* && "${searchType}" == *"Only"* ]]; then
        echo "[Info] Searching for folders only"
        # Search for folders only
        # Use timeout to prevent the find command from running indefinitely
        foundPath=$(timeout "${timeout}" find "$parentSearchPath" -type d -name "$leafSearchName" 2>/dev/null)
        exitcode=$?
        if [[ $exitcode -eq 0 || $exitcode -eq 124 ]]; then
            if [[ -n $foundPath ]]; then
                echo "[Alert] File Found"
            fi
        fi
    elif [[ "${searchType}" == *"Files"* && "${searchType}" == *"Folders"* ]]; then
        echo "[Info] Searching for files or folders"
        # Search for files or folders
        # Use timeout to prevent the find command from running indefinitely
        foundPath=$(timeout "${timeout}" find "$parentSearchPath" -name "$leafSearchName" 2>/dev/null)
        exitcode=$?
        if [[ $exitcode -eq 0 || $exitcode -eq 124 ]]; then
            if [[ -n $foundPath ]]; then
                echo "[Alert] File Found"
            fi
        fi
    else
        echo "[Error] Invalid search type"
        echo "Valid search types: Files Only, Folders Only, Files Or Folders"
        exit 1
    fi
elif [[ -z "${leafSearchName}" ]]; then
    echo "[Info] Searching in path only"
    # Search in path only
    # Use timeout to prevent the find command from running indefinitely
    foundPath=$(timeout "${timeout}" find "$parentSearchPath")
    exitcode=$?
    if [[ $exitcode -eq 0 || $exitcode -eq 124 ]]; then
        if [[ -n $foundPath ]]; then
            echo "[Alert] File Found"
        fi
    fi
fi

# Check exit code
if [[ -n $foundPath ]]; then
    # Split the string into an array
    IFS=$'\n' read -rd '' -a foundPathArray <<<"${foundPath}"
    # Print each element of the array
    for element in "${foundPathArray[@]}"; do
        echo "[Alert] ${element} exists"
    done
elif [[ -z $foundPath ]]; then
    echo "[Warn] Could not find a file or folder"
    exit 1
else
    # If the find command fails to find the file or folder

    # Figure out the grammer for the search type
    if [[ "${searchType}" == *"Only"* ]]; then
        if [[ "${searchType}" == *"Files"* ]]; then
            searchTypeInfo="file"
        elif [[ "${searchType}" == *"Folders"* ]]; then
            searchTypeInfo="folder"
        fi
    elif [[ "${searchType}" == *"Files"* && "${searchType}" == *"Folders"* ]]; then
        searchTypeInfo="file or folder"
    fi
    echo "[Info] Could not find a ${searchTypeInfo} in the path ${parentSearchPath} with the name containing: ${leafSearchName}"
fi

# If foundPath contains "Alarm clock:" then the command timed out
if [[ "${foundPath}" == *"Alarm clock:"* ]]; then
    echo "[Alert] Timed out searching for file or folder"
    # Remove "Alarm clock: *" from the string
    foundPath=${foundPath/Alarm clock: [0-9]*//}
fi

# If command times out
if [[ $exitcode -ge 124 && $exitcode -le 127 || $exitcode -eq 137 ]]; then
    echo "[Alert] Timed out searching for file or folder"
    echo "timeout exit code: $exitcode"
    echo "  124  if COMMAND times out, and --preserve-status is not specified"
    echo "  125  if the timeout command itself fails"
    echo "  126  if COMMAND is found but cannot be invoked"
    echo "  127  if COMMAND cannot be found"
    echo "  137  if COMMAND (or timeout itself) is sent the KILL (9) signal (128+9)"
    echo "find command result: $foundPath"
    exit 1
fi

# Save to custom field
if [[ -n "${customField}" && "${customField}" != "null" ]]; then
    SetCustomField "${customField}" "${foundPath}"
fi

 

Análisis detallado

El script para comprobar la presencia de archivos funciona tomando varios parámetros para personalizar el proceso de búsqueda. Aquí tienes un desglose detallado de su funcionalidad:

1. Parámetros e inicialización:

  • –path: Especifica el directorio base en el que buscar.
  • –name: Define el nombre del archivo o carpeta a buscar, admitiendo comodines.
  • –type: Determina si se buscan archivos, carpetas o ambos.
  • –timeout: Establece el tiempo máximo para la operación de búsqueda, por defecto 30 minutos.
  • –customfield: Permite guardar el resultado de la búsqueda en un campo personalizado.

2. Análisis de argumentos: El script para comprobar la presencia de archivos analiza los argumentos de la línea de comandos para inicializar los parámetros de búsqueda. Si falta algún parámetro requerido, proporciona un mensaje de error y sale.

3. Ejecución de la búsqueda:

  • El script para comprobar la presencia de archivos resuelve cualquier enlace simbólico en la ruta de búsqueda.
  • Verifica que la ruta especificada existe y es válida.
  • Garantiza que el tiempo de espera está dentro del rango aceptable (de 1 a 120 minutos).

4. Búsqueda de archivos o carpetas: Dependiendo del tipo especificado (archivos, carpetas o ambos), el script para comprobar la presencia de archivos utiliza el comando find con un tiempo de espera para localizar los elementos deseados. Si se encuentra, avisa al usuario y, opcionalmente, guarda el resultado en un campo personalizado.

5. Tratamiento de errores e informes: El script para comprobar la presencia de archivos incluye un completo tratamiento de errores, garantizando que problemas como rutas no válidas, valores de tiempo de espera incorrectos o archivos/carpetas inexistentes se comuniquen claramente al usuario.

Posibles casos de uso

Estudio de caso: Verificación de la conformidad informática

Un profesional de TI es responsable de garantizar que los archivos de configuración de seguridad críticos estén presentes en todos los servidores. Con este script para comprobar la presencia de archivos y carpetas, puede automatizar el proceso de verificación:

1. Configuración:

  • –path: /etc/security
  • –name: security.conf
  • –type: Sólo archivos
  • –timeout: 10

2. Ejecución: El script para comprobar la presencia de archivos y carpetas busca el archivo security.conf en la ruta especificada dentro del periodo de tiempo establecido. Si se encuentra, registra una alerta; si no, notifica al profesional de TI, lo que permite una rápida remediación.

Comparaciones

En comparación con la verificación manual o el uso de comandos básicos del shell, este script para comprobar la presencia de carpetas ofrece varias ventajas:

  • Automatización: Reduce la necesidad de controles manuales.
  • Gestión del tiempo de espera: Evita búsquedas prolongadas imponiendo un tiempo de espera.
  • Informes personalizados: Permite guardar los resultados en campos personalizados para su posterior procesamiento o para la elaboración de informes de cumplimiento.

Otros métodos, como el uso de ls o comandos de prueba en Bash, carecen de estas características avanzadas, por lo que este script para comprobar la presencia de archivos es una solución más robusta y eficiente.

FAQ

  1. ¿Qué ocurre si se agota el tiempo de espera del script para comprobar la presencia de archivos?
    El script informa de un tiempo de espera y sale con un código de error apropiado, asegurándose de que el usuario es consciente de que la búsqueda estaba incompleta.
  2. ¿Puedo buscar varios tipos de archivos simultáneamente?
    No, actualmente el script para comprobar la presencia de archivos permite buscar archivos o carpetas basándose en un único patrón de nombre cada vez.
  3. ¿Cómo se gestionan los enlaces simbólicos en la ruta de búsqueda?
    El script para comprobar la presencia de archivos resuelve automáticamente los enlaces simbólicos, asegurando que la búsqueda se realiza en el directorio correcto.

Implicaciones

El uso de este script para comprobar la presencia de archivos puede mejorar significativamente la seguridad informática al garantizar la presencia de archivos y carpetas críticos. La verificación automatizada ayuda a mantener el cumplimiento de las políticas de seguridad y reduce el riesgo de omitir archivos importantes, lo que podría provocar vulnerabilidades o fallos en el sistema.

Recomendaciones

  • Realiza auditorías de forma periódica: Programa la ejecución periódica del script para mantener actualizada la verificación de los archivos críticos.
  • Utiliza campos personalizados: Aprovecha la opción de campos personalizados para realizar un seguimiento e informar sistemáticamente de los resultados de búsqueda.
  • Ajusta el tiempo de espera: Ajusta el parámetro de tiempo de espera en función del tamaño previsto del directorio y del rendimiento del sistema para evitar retrasos innecesarios.

Reflexiones finales

Este script Bash es una potente herramienta para los profesionales de TI, ya que proporciona un método automatizado y fiable para comprobar la presencia de archivos y carpetas. Al integrar este script para comprobar la presencia de archivos en las comprobaciones rutinarias, los MSP pueden garantizar una mayor eficacia y seguridad en sus operaciones. Herramientas como NinjaOne pueden mejorar aún más este proceso al ofrecer soluciones integrales de gestión de TI, lo que facilita la implementación, supervisión y gestión de scripts en múltiples sistemas.

La automatización de la gestión de archivos no sólo ahorra tiempo, sino que también aumenta la precisión, garantizando que los archivos críticos estén siempre donde tienen que estar.

Categorías:

Quizá también te interese…

×

¡Vean a NinjaOne en acción!

Al enviar este formulario, acepto la política de privacidad de NinjaOne.

Términos y condiciones de NinjaOne

Al hacer clic en el botón “Acepto” que aparece a continuación, estás aceptando los siguientes términos legales, así como nuestras Condiciones de uso:

  • Derechos de propiedad: NinjaOne posee y seguirá poseyendo todos los derechos, títulos e intereses sobre el script (incluidos los derechos de autor). NinjaOne concede al usuario una licencia limitada para utilizar el script de acuerdo con estos términos legales.
  • Limitación de uso: solo podrás utilizar el script para tus legítimos fines personales o comerciales internos, y no podrás compartirlo con terceros.
  • Prohibición de republicación: bajo ninguna circunstancia está permitido volver a publicar el script en ninguna biblioteca de scripts que pertenezca o esté bajo el control de cualquier otro proveedor de software.
  • Exclusión de garantía: el script se proporciona “tal cual” y “según disponibilidad”, sin garantía de ningún tipo. NinjaOne no promete ni garantiza que el script esté libre de defectos o que satisfaga las necesidades o expectativas específicas del usuario.
  • Asunción de riesgos: el uso que el usuario haga del script corre por su cuenta y riesgo. El usuario reconoce que existen ciertos riesgos inherentes al uso del script, y entiende y asume cada uno de esos riesgos.
  • Renuncia y exención: el usuario no hará responsable a NinjaOne de cualquier consecuencia adversa o no deseada que resulte del uso del script y renuncia a cualquier derecho o recurso legal o equitativo que pueda tener contra NinjaOne en relación con su uso del script.
  • CLUF: si el usuario es cliente de NinjaOne, su uso del script está sujeto al Contrato de Licencia para el Usuario Final (CLUF).