Concatenar archivos por valores de tabla

Concatenar archivos por valores de tabla

Tengo varios archivos, cada uno de los cuales contiene un patrón específico en sus nombres, ABC1234001que contienen información sobre ciertos grupos de mis datos (tablas de varias columnas). También tengo una tabla info.tsvcomo esta:

group1    ABC1234001    ABC1234010
group2    ABC1234011    ABC1234018
group3    ABC1234019    ABC1234028
...       ...           ...

Contiene:

  • columna "grupo", que especifica el grupo,
  • columna "primer archivo", que especifica el patrón para el primer archivo (orden alfabético) que contiene información para el grupo correspondiente,
  • Columna "último archivo", que especifica el patrón para el último archivo (orden alfabético) que contiene información para el grupo correspondiente.

Entonces, lo que tengo que hacer es combinar archivos para cada grupo en un solo archivo, tal como

cat ABC123401{1..8}* >> group2.tsv

sería para el grupo2 como ejemplo, mientras lee este info.tsvarchivo. En este ejemplo dado, todos los archivos ( ,,,,,,,,, ABC1234011.tsv) están concatenados en un archivo .ABC1234012.tsvABC1234013.tsvABC1234014.tsvABC1234015.tsvABC1234016.tsvABC1234017.tsvABC1234018.tsvgroup2.tsv

Lo que voy a hacer es lo siguiente:

while read $file; do
  #assign columns to variables like $1="group", $2="firstfile", $3="lastfile"
  cat *{$2..$3}* > $1.tsv;
done < info.tsv

Pero no estoy muy seguro de cómo cambiar de forma iterativa las variables para este enfoque. Quizás usar awksea más útil, pero no lo sé. El script debería producir un montón de archivos llamados group1.tsv, group2.tsvque contienen el contenido de los archivos correspondientes desde el "primer archivo" hasta el "último archivo" en la tabla. Por favor ayúdenme a escribir el guión para hacerlo.

Respuesta1

El siguiente script asume que todos los archivos que desee concatenar coinciden con el patrón *.tsv. Si sabe que todos coinciden ABC*.tsv, es posible que desee utilizar ese patrón al inicio del script en lugar de *.tsv.

Además, el script supone que todos los nombres de archivos que pertenecen a un grupo específico se generan como una sublista continua de la lista que *.tsvse expande.

#!/bin/sh

set -- *.tsv

while read -r group first last; do
        collect=false

        for name do
                if ! "$collect"; then
                        [ "$name" = "$first.tsv" ] || continue
                        collect=true
                fi

                if "$collect"; then
                        cat -- "$name"
                        [ "$name" = "$last.tsv" ] && break
                fi
        done >"$group.tsv"

done <info.tsv

El script establece la lista de parámetros posicionales en la lista de nombres coincidentes *.tsv. Luego lee los tres campos de cada línea en las info.tsvvariables groupy first.last

Para cada línea leída de info.tsvesta manera, la lista de parámetros posicionales se escanea en busca de nombres que coincidan con el primer nombre del grupo. Una vez que se encuentra este nombre, configuramos una bandera, collectque le indica a la lógica del script que comience a recopilar los datos de los archivos nombrados en la lista de parámetros posicionales, desde la posición actual en la lista. Esto finaliza una vez que nos topamos con un nombre que corresponde al apellido de un grupo.

Tenga en cuenta que truey falseaquí se utilizan como comandos y no como cadenas simples. El valor almacenado en la variable $collectse está ejecutando, if ! "$collect"lo que significa que el script ejecutará uno de los dos comandos integrados del shell trueo false. El shell no tiene palabras clave especiales para verdadero o falso como lo hacen otros lenguajes (por ejemplo, Python).

Pruebas:

$ ls
script
$ touch ABC{1234001..1234030}.tsv
$ for name in ABC*.tsv; do printf 'Name: %s\n' "$name" >"$name"; done
$ cat ABC1234015.tsv
Name: ABC1234015.tsv
$ cat >info.tsv <<END_DATA
group1 ABC1234001 ABC1234010
group2 ABC1234025 ABC1234030
END_DATA
$ ./script
$ cat group1.tsv
Name: ABC1234001.tsv
Name: ABC1234002.tsv
Name: ABC1234003.tsv
Name: ABC1234004.tsv
Name: ABC1234005.tsv
Name: ABC1234006.tsv
Name: ABC1234007.tsv
Name: ABC1234008.tsv
Name: ABC1234009.tsv
Name: ABC1234010.tsv
$ cat group2.tsv
Name: ABC1234025.tsv
Name: ABC1234026.tsv
Name: ABC1234027.tsv
Name: ABC1234028.tsv
Name: ABC1234029.tsv
Name: ABC1234030.tsv

Como se mencionó en los comentarios de esta respuesta, la forma en que desarrollaría este script para mi uso personal sería dejar el script con este aspecto:

#!/bin/sh

while read -r group first last; do
        collect=false

        for name do
                filename=$( basename "$name" )

                if ! "$collect"; then
                        [ "$filename" = "$first.tsv" ] || continue
                        collect=true
                fi

                if "$collect"; then
                        cat -- "$name"
                        [ "$filename" = "$last.tsv" ] && break
                fi
        done >"$group.tsv"

done

Tenga en cuenta la eliminación del setcomando en la parte superior (esto será reemplazado por argumentos de la línea de comando) y la eliminación de la redirección de info.tsv(esto será reemplazado por una redirección en la línea de comando). También introduje una filenamevariable que contendrá el componente de nombre de archivo de las rutas proporcionadas en la línea de comando.

Luego ejecutaría el script así:

$ ./script ABC*.tsv <info.tsv

Lo que he logrado con esto es un script que es independiente de dónde se almacena la lista del grupo de entrada o cómo se llama, y ​​a eso no le importa cómo ABCse llaman los archivos (siempre que tengan un .tsvsufijo de nombre de archivo) o dónde están almacenados. .

Respuesta2

Su enfoque es una buena idea, pero desafortunadamente no funcionará porque las variables no se expanden dentro de las expansiones de llaves:

$ echo {1..5}
1 2 3 4 5
$ a=1
$ b=5
$ echo {$a..$b}
{1..5}

Puedes solucionar esto usando eval:

sed 's/ABC//g' info.tsv | 
    while read -r group start end; do 
        files=( $(eval echo ABC{$start..$end}.tsv) )
        cat "${files[@]}" > "$group.tsv"; 
    done 

Esto primero eliminará todas las instancias de ABCdel info.tsvarchivo para que podamos obtener los números solos. Tenga en cuenta que esto supone la estructura de datos exacta que nos ha mostrado. Si ABCtambién puede estar presente en el nombre del grupo, esto se interrumpirá.

Después de eliminar ABC, el resultado se canaliza al whilebucle que lee tres variables $group: $starty $end. Luego se pasan a evallo que expandirá la variable antes de llamar a la expansión de llaves, lo que le permitirá obtener una lista de nombres de archivos:

$ eval echo ABC{1..5}
ABC1 ABC2 ABC3 ABC4 ABC5

El resultado de evalse almacena en la $filesmatriz, que se pasa como entrada a cat:

cat "${files[@]}" > "$group.tsv";

Respuesta3

Si te entiendo bien aquí tienes una opción.

$ while IFS= read -r i; do
    f=$(echo "$i" | cut -d' ' -f1)
    cat $(echo "$i" | cut -d' ' -f2- | sed -E 's/([0-9])\s+/\1.tsv /;s/([0-9])$/\1.tsv /') > "$f.txt"
  done < info.tsv

  • f=$(echo "$i" | cut -d' ' -f1)recupera el nombre del grupo.
  • cat $(cut -d' ' -f2- | sed -E 's/([0-9])\s+|([0-9])$/\1.tsv /g')concatena la lista de archivos en la línea.

información relacionada