Probablemente sea más fácil combinar los tres primeros campos con awk:
awk '{print $1 "_" $2 "_" $3 " " $4}' filename
Entonces puedes usar join
normalmente en el "campo 1"
Puedes probar esto
awk '{
o1=$1;o2=$2;o3=$3
$1=$2=$3="";gsub(" +","")
_[o1 FS o2 FS o3]=_[o1 FS o2 FS o3] FS $0
}
END{ for(i in _) print i,_[i] }' file1 file2
salida
$ ./shell.sh
foo 1 scaf 3 4.5
bar 2 scaf 3.3 1.00
foo 1 boo 2.3
Si desea omitir líneas poco comunes
awk 'FNR==NR{
s=""
for(i=4;i<=NF;i++){ s=s FS $i }
_[$1$2$3] = s
next
}
{
printf $1 FS $2 FS $3 FS
for(o=4;o<NF;o++){
printf $i" "
}
printf $NF FS _[$1$2$3]"\n"
} ' file2 file1
salida
$ ./shell.sh
foo 1 scaf 3 4.5
bar 2 scaf 3.3 1.00
Aquí está el correcto respuesta (en términos de uso de GNU coreutils estándar herramientas y no escribir secuencias de comandos personalizadas en perl/awk lo que quieras).
$ join -j1 -o1.2,1.3,1.4,1.5,2.5 <(<file1 awk '{print $1"-"$2"-"$3" "$0}' | sort -k1,1) <(<file2 awk '{print $1"-"$2"-"$3" "$0}' | sort -k1,1)
bar 2 scaf 3.3 1.00
foo 1 scaf 3 4.5
Bien, ¿cómo funciona?:
-
En primer lugar utilizaremos una gran herramienta
join
que puede fusionar dos líneas.join
tiene dos requisitos:- Podemos unirnos solo por un solo campo.
- Ambos archivos deben estar ordenados por columna clave!
-
Necesitamos generar claves en archivos de entrada y para eso usamos un simple
awk
guión:$ cat file1 foo 1 scaf 3 bar 2 scaf 3.3 $ <file1 awk '{print $1"-"$2"-"$3" "$0}' foo-1-scaf foo 1 scaf 3 bar-2-scaf bar 2 scaf 3.3
Verá, agregamos la primera columna con alguna clave como "foo-1-scaf ".Hacemos lo mismo con archivo2 .POR CIERTO.
<file awk
, es una forma elegante de escribirawk file
, ocat file | awk
.También debemos ordenar nuestros archivos por la clave, en nuestro caso esta es la columna 1, por lo que agregamos al final del comando el
| sort -k1,1
(ordenar por texto de la columna 1 a la columna 1) -
En este punto, solo podríamos generar archivos file1.with.key y archivo2.con.clave y únase a ellos, pero supongamos que esos archivos son enormes, no queremos copiarlos en el sistema de archivos. En su lugar, podemos usar algo llamado
bash
Procesar la sustitución para generar la salida en la canalización con nombre (esto evitará la creación de archivos intermedios innecesarios). Para obtener más información, lea el enlace provisto.Nuestra sintaxis objetivo es:
join <( some command ) <(some other command)
-
Lo último es explicar los argumentos de combinación sofisticados:
-j1 -o1.2,1.3,1.4,1.5,2.5
-j1
- unirse por clave en la primera columna (en ambos archivos)-
-o
- mostrar solo esos campos1.2
(1er archivo campo2),1.3
(1er archivo columna 3), etc.De esta manera unimos líneas, pero
join
genera solo las columnas necesarias.
Las lecciones aprendidas de esta publicación deberían ser:
- debes dominar las coreutils paquete, esas herramientas son muy poderosas cuando se combinan y casi nunca es necesario escribir un programa personalizado para tratar estos casos,
- Las herramientas básicas de utilidades también son ultrarrápidas y están muy probadas, por lo que siempre son la mejor opción.
El comando unir es difícil de usar y solo se une en una columna
La experimentación extensiva más el escrutinio minucioso de las páginas del manual indican que no puede unir directamente varias columnas, y todos mis ejemplos prácticos de unión, curiosamente, usan solo una columna de unión.
En consecuencia, cualquier solución requerirá que las columnas que se unirán se concatenen en una sola columna, de alguna manera. El comando de unión estándar también requiere que sus entradas estén en el orden correcto:hay un comentario en la unión de GNU (unión de información coreutils) acerca de que no siempre requiere datos ordenados:
Sin embargo, como una extensión de GNU, si la entrada no tiene líneas que no se puedan emparejar, el orden de clasificación puede ser cualquier orden que considere que dos campos son iguales si y solo si la comparación de clasificación descrita anteriormente los considera iguales.
Una forma posible de hacerlo con los archivos dados es:
awk '{printf("%s:%s:%s %s %s %s %s\n", $1, $2, $3, $1, $2, $3, $4);}' file1 |
sort > sort1
awk '{printf("%s:%s:%s %s %s %s %s\n", $1, $2, $3, $1, $2, $3, $4);}' file2 |
sort > sort2
join -1 1 -2 1 -o 1.2,1.3,1.4,1.5,2.5 sort1 sort2
Esto crea un campo de ordenación compuesto al principio, usando ':' para separar los subcampos, y luego ordena el archivo, para cada uno de los dos archivos. Luego, el comando de unión une los dos campos compuestos, pero imprime solo los campos no compuestos (no unidos).
La salida es:
bar 2 scaf 3.3 1.00
foo 1 scaf 3 4.5
Intentos fallidos de hacer que la unión haga lo que no hará
unirse -1 1 -2 1 -1 2 -2 2 -1 3 -2 3 -o 1.1,1.2,1.3,1.4,2.4 archivo1 archivo2
En MacOS X 10.6.3, esto da:
$ cat file1
foo 1 scaf 3
bar 2 scaf 3.3
$ cat file2
foo 1 scaf 4.5
foo 1 boo 2.3
bar 2 scaf 1.00
$ join -1 1 -2 1 -1 2 -2 2 -1 3 -2 3 -o 1.1,1.2,1.3,1.4,2.4 file1 file2
foo 1 scaf 3 4.5
bar 2 scaf 3.3 4.5
$
Esto se une en el campo 3 (solo), que no es lo que se desea.
Debe asegurarse de que los archivos de entrada estén en el orden correcto.