
Tengo varios archivos, cada uno de los cuales contiene un patrón específico en sus nombres, ABC1234001
que contienen información sobre ciertos grupos de mis datos (tablas de varias columnas). También tengo una tabla info.tsv
como 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.tsv
archivo. En este ejemplo dado, todos los archivos ( ,,,,,,,,, ABC1234011.tsv
) están concatenados en un archivo .ABC1234012.tsv
ABC1234013.tsv
ABC1234014.tsv
ABC1234015.tsv
ABC1234016.tsv
ABC1234017.tsv
ABC1234018.tsv
group2.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 awk
sea más útil, pero no lo sé. El script debería producir un montón de archivos llamados group1.tsv
, group2.tsv
que 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 *.tsv
se 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.tsv
variables group
y first
.last
Para cada línea leída de info.tsv
esta 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, collect
que 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 true
y false
aquí se utilizan como comandos y no como cadenas simples. El valor almacenado en la variable $collect
se está ejecutando, if ! "$collect"
lo que significa que el script ejecutará uno de los dos comandos integrados del shell true
o 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 set
comando 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 filename
variable 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 ABC
se llaman los archivos (siempre que tengan un .tsv
sufijo 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 ABC
del info.tsv
archivo para que podamos obtener los números solos. Tenga en cuenta que esto supone la estructura de datos exacta que nos ha mostrado. Si ABC
también puede estar presente en el nombre del grupo, esto se interrumpirá.
Después de eliminar ABC
, el resultado se canaliza al while
bucle que lee tres variables $group
: $start
y $end
. Luego se pasan a eval
lo 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 eval
se almacena en la $files
matriz, 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.