Dividir archivo csv usando Automator Service (menú contextual del buscador)

Estoy tratando de crear un servicio de Automator para el menú contextual del botón derecho del Finder que puede dividir cualquier archivo csv seleccionado, mientras copia el encabezado original en la parte superior de cada archivo.

Mi intento actual es hacer que Automator ejecute este Bash Shell Script :

#!/bin/bash

FILE=$(ls -1 | grep MY_CSV_FILE.csv)
NAME=${FILE%%.csv}

head -1 $FILE > header.csv
tail -n +2 $FILE > data.csv

split -l 50 data.csv

for a in x??
    do
        cat header.csv $a > $NAME.$a.csv
    done

rm header.csv data.csv x??

Este script se dividirá MY_CSV_FILE.csven archivos nuevos con un máximo de 50 líneas mientras se copia el encabezado original en la parte superior de cada archivo. Los nuevos archivos tendrán el nombre original anexado con xaa, xab, xacetc.

Con respecto a la configuración de Automator, este es el servicio en el que estoy trabajando actualmente. El problema en este momento es que no puedo pasar el archivo seleccionado en Finder al script Bash.

ingrese la descripción de la imagen aquí

Darse cuenta de:

  • El servicio recibe: archivos o carpetas en Finder.app .
  • Pase la entrada al script de Shell: como argumentos .
  • Lo eliminé #!/bin/bashde la parte superior del Shell Script y configuré el Shell en: /bin/bash .
  • Cambié MY_CSV_FILE.csvpor "$f"- no estoy seguro si eso es correcto.

¿También necesito especificar la ruta usando algo como tanto "$@"para el archivo de entrada como para los archivos de salida resultantes? No he hecho algo como esto antes, así que no estoy muy familiarizado con esa variable y "$f"para el caso.

¿Cómo podría hacer que esto funcione? Me gustaría que los archivos resultantes aparezcan en la misma carpeta que el archivo que seleccioné para ejecutar el Servicio, a través del menú contextual del Finder. Sería aún mejor si el Servicio solo aceptara archivos csv.

ingrese la descripción de la imagen aquí

¿ Puede editar su pregunta para aclarar qué es lo que no funciona en su intento de enfoque? Es decir, ¿te da un error? ¿O funciona, pero no de la forma que deseas? ¿O es que le gustaría que hiciera algo además de lo que realmente está haciendo ahora? ¿Algo más? Además, ¿podemos suponer que el Shell Script real al que hace referencia funciona, o es ese su problema?
@Monomeeth Un punto muy válido, lo siento. Al ejecutar el servicio, apareció un cuadro de error de macOS que decía "Error de script de Shell" o algo así. No logró nada cercano al éxito. No vi ningún error de código específico en Automator. También parece haber creado un archivo llamado x??.csven mi carpeta de usuario ( ~).

Respuestas (1)

Escribiría el código un poco diferente, y aquí hay un ejemplo de cómo lo haría:

#!/bin/bash

for f in "$@"; do
    if [[ -f $f ]]; then
        d="$(dirname "$f")"
        n="$(basename "$f")"
        t='/tmp'
        if [[ ${n##*.} =~ [cC][sS][vV] ]]; then
            head -1 "$f" > $t/h.tmp
            tail -n +2 "$f" | split -a 3 -l 50 - $t/tmp.
            i=1
            for s in $t/tmp.a??; do
                fn="$d/${n%.*}.$(printf '%03d' $i).csv"
                if [[ ! -f $fn ]]; then 
                    cat $t/h.tmp $s > "$fn"
                    ((i++))
                else
                    rm $t/h.tmp $t/tmp.a??
                    echo "The file '"$fn"' already exists!"
                    exit
                fi
            done
            rm $t/h.tmp $t/tmp.a??
            echo ''
        fi
    fi
done
  • Tal como está codificado actualmente, maneja uno o más archivos pasados ​​al servicio .
  • Se asegura de que el objeto sobre el que se actúa sea un archivo , no un directorio .
  • Asegúrese de que el archivo tenga una extensión .csv (independientemente del caso de la extensión).
  • Crea los archivos temporales en:/tmp
  • Comprueba que el nombre del archivo de salida no exista y, si existe, se limpia y se cierra.
  • Escribe en un archivo con un nombre de archivo incrementado numéricamente , por ejemplo file.001.csv, file.002.csv, etc., en el mismo directorio que los archivos pasados ​​al servicio .
  • Elimina los archivos temporales creados en:/tmp
  • Tal como está codificado actualmente, maneja archivos con un número de líneas de hasta 49.950 archivos divididos en 50 líneas, sin contar el encabezado.
    • Tenga en cuenta que no se codifica ningún manejo de errores para el recuento total de líneas del archivo de origen ; sin embargo, se podría agregar fácilmente.
    • O modifique fácilmente para manejar archivos con un recuento de líneas de hasta 499,950 archivos divididos en 50 líneas, sin contar el encabezado, cambiando -a 3el split comando a -a 4y '%03d'del printf comando a '%04d'. También cambiarías $t/tmp.a??en
      for s in $t/tmp.a??; doy rm $t/h.tmp $t/tmp.a??para:$t/tmp.a???

También agregaría una acción Ejecutar Apple Script al servicio , con el siguiente código :

on run {input, parameters}
    if (item 1 of input) is "" then
        display notification "Splitting of the target file(s) is finished!" with title "Split CSV File(s)"
    else
        display notification (item 1 of input as string) with title "Split CSV File(s)"
    end if
end run

Esto habilita la salida de los echo comandos en la acción Ejecutar script de shell para mostrar una notificación si ya existe un archivo de salida o cuando finaliza la división.

Tenga en cuenta que si bien la notificación se puede realizar desde la acción Ejecutar script de Shell usando , no obstante, lo hice de esta manera porque era más fácil de codificar.osascript


Flujo de trabajo de servicio de Automator

Esto se probó en un archivo llamado file.csv en Finder , que tiene 200 líneas, y las imágenes a continuación muestran lo que fue creado por la parte de la acción Run Shell Script del servicio Automator cuando se ejecuta en el archivo .

Archivo CSV en Finder

Contenido del archivo CSV dividido en TextEdit

Bravo, este es un trabajo muy impresionante y una gran mejora en el guión original. Hay mucho que desempaquetar aquí, así que tendré que hacerlo a su debido tiempo, pero puedo confirmar que el Servicio funciona muy bien en las primeras pruebas. Por curiosidad (no es necesario), ¿hay alguna razón para codificar 49 950 en lugar de 499 950 líneas, por ejemplo, rendimiento? Probablemente nunca necesitaré este último, pero es interesante aprenderlo.
@Winterflags, casi siempre hay más de una forma de hacer algo y por qué comencé con " ... aquí hay un ejemplo... ". Con el código del OP, los nombres de archivo divididos iban a incrementarse por el valor de $a, es decir x??, por ejemplo xaa, xabetc. Es una preferencia personal para mí, quiero un valor numérico en los nombres de archivo divididos, así que lo codifiqué para eso. . En el código del OP, for a in x??habría fallado si el archivo fuente tuviera un recuento de líneas superior a 49,950 dividido en 50 archivos de línea porque x??solo cuenta para 999 archivos donde x???cuenta para 9999 archivos.
@Winterflags, tanto el código en el OP como mi código sufren de la misma limitación de codificación fija, porque como están codificados , ambos asumen que no habrá más archivos 999 creados por el comando split . hacer los cálculos primero o los cálculos se pueden hacer primero y codificar para adaptarse dinámicamente. Se necesita menos codificación para ajustar el pad y luego verificar primero el conteo de líneas. Si desea garantizar una cantidad muy alta de líneas, incremente los valores aún más que como se indica en mi respuesta para que el pad sea más que adecuado.
@Winterflags, el cambio -a 3del split comando a -a 5, y '%03d'del printf comando a '%04d'y $t/tmp.a??en for s in $t/tmp.a??; doy rm $t/h.tmp $t/tmp.a??a $t/tmp.a????, manejará archivos con un recuento de líneas de hasta 4,999,950 archivos divididos en 50 líneas. Para hacerlo aún más fácil, $t/tmp.a??se puede configurar en $t/tmp.*y luego solo tendrá que modificar -a 3el split comando y '%03d'el printf comando configurando cada uno en un número más alto, por ejemplo, -a 8y '%08d'manejará archivos con un recuento de líneas de hasta 4,999,999,950 dividido en 50 líneas archivos
@Winterflags, el punto principal es que quería que supieras que no hubo un manejo de errores en torno a esto y cómo ajustar qué tener en cuenta. Si bien dije " ... sin embargo, podría agregarse fácilmente ", eso realmente debería haber sido " ... sin embargo, podría agregarse ". y por qué cambiar los valores como se mencionó es en realidad lo más fácil de hacer.