Respuestas:
Para la cut(1)
página del manual:
Use uno, y solo uno de -b, -c o -f. Cada LISTA está compuesta por un rango, o muchos rangos separados por comas. La entrada seleccionada se escribe en el mismo orden en que se lee y se escribe exactamente una vez.
Primero llega al campo 1, de modo que se imprime, seguido del campo 2.
Use en su awk
lugar:
awk '{ print $2 " " $1}' file.txt
FS
es una opción, OFS
es una variable. egawk -v OFS=";" -F"\t" '{print $2,$1}'
| sed 's/\r//' |
antes de awk
awk '{print $4 "\t" $2 "\t" $6 "\t" $7}' file
También puede combinar cut
y paste
:
paste <(cut -f2 file.txt) <(cut -f1 file.txt)
a través de comentarios: es posible evitar bashisms y eliminar una instancia de corte haciendo:
paste file.txt file.txt | cut -f2,3
cut
funciona bien para columnas de longitud variable siempre que tenga un separador de columna único.
bash
ismos y eliminar una instancia de cut
haciendo: paste file.txt file.txt | cut -f2,3
usando solo el caparazón,
while read -r col1 col2
do
echo $col2 $col1
done <"file"
"$col2"
y "$col1"
, podría haber metacaracteres de shell u otras travesuras en los datos.
Puedes usar Perl para eso:
perl -ane 'print "$F[1] $F[0]\n"' < file.txt
La ventaja de ejecutar perl es que (si conoce a Perl) puede hacer muchos más cálculos en F que reorganizar columnas.
perl -ae print
funciona cat
para mí
Utilizando join
:
join -t $'\t' -o 1.2,1.1 file.txt file.txt
Notas:
-t $'\t'
En GNU join
el más intuitivo -t '\t'
y sin la $
falla, ( coreutils v8.28 y anteriores?); Probablemente sea un error que una solución alternativa $
debería ser necesaria. Ver: unix join separator char .
join
necesita dos nombres de archivo, aunque solo se esté trabajando en un archivo. Usar el mismo nombre dos veces engaña join
para realizar la acción deseada.
Para sistemas con bajos recursos join
ofrece una huella más pequeña que algunas de las herramientas utilizadas en otras respuestas:
wc -c $(realpath `which cut join sed awk perl`) | head -n -1
43224 /usr/bin/cut
47320 /usr/bin/join
109840 /bin/sed
658072 /usr/bin/gawk
2093624 /usr/bin/perl
Acabo de trabajar en algo muy similar, no soy un experto, pero pensé que compartiría los comandos que he usado. Tenía un csv de varias columnas del que solo necesitaba 4 columnas y luego necesitaba reordenarlas.
Mi archivo era pipe '|' delimitado pero eso se puede cambiar.
LC_ALL=C cut -d$'|' -f1,2,3,8,10 ./file/location.txt | sed -E "s/(.*)\|(.*)\|(.*)\|(.*)\|(.*)/\3\|\5\|\1\|\2\|\4/" > ./newcsv.csv
¡Es cierto que es realmente duro y listo, pero se puede ajustar para adaptarse!
Usando sed
Use sed con subexpresiones anidadas de expresiones regulares básicas para capturar y reordenar el contenido de la columna. Este enfoque es más adecuado cuando hay un número limitado de cortes para reordenar columnas, como en este caso.
La idea básica es rodear porciones interesantes del patrón de búsqueda con \(
y \)
, que se puede reproducir en el patrón de reemplazo con \#
where #
representa la posición secuencial de la subexpresión en el patrón de búsqueda.
Por ejemplo:
$ echo "foo bar" | sed "s/\(foo\) \(bar\)/\2 \1/"
rendimientos:
bar foo
El texto fuera de una subexpresión se escanea pero no se retiene para reproducirlo en la cadena de reemplazo.
Aunque la pregunta no discutió columnas de ancho fijo, discutiremos aquí ya que esta es una medida digna de cualquier solución planteada. Para simplificar, supongamos que el archivo está delimitado por espacios, aunque la solución se puede ampliar para otros delimitadores.
Espacios colapsados
Para ilustrar el uso más simple, supongamos que se pueden contraer múltiples espacios en espacios individuales, y los valores de la segunda columna se terminan con EOL (y no con espacio).
Expediente:
bash-3.2$ cat f
Column1 Column2
str1 1
str2 2
str3 3
bash-3.2$ od -a f
0000000 C o l u m n 1 sp sp sp sp C o l u m
0000020 n 2 nl s t r 1 sp sp sp sp sp sp sp 1 nl
0000040 s t r 2 sp sp sp sp sp sp sp 2 nl s t r
0000060 3 sp sp sp sp sp sp sp 3 nl
0000072
Transformar:
bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f
Column2 Column1
1 str1
2 str2
3 str3
bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f | od -a
0000000 C o l u m n 2 sp C o l u m n 1 nl
0000020 1 sp s t r 1 nl 2 sp s t r 2 nl 3 sp
0000040 s t r 3 nl
0000045
Preservar anchos de columna
Extendamos ahora el método a un archivo con columnas de ancho constante, mientras permitimos que las columnas sean de diferentes anchos.
Expediente:
bash-3.2$ cat f2
Column1 Column2
str1 1
str2 2
str3 3
bash-3.2$ od -a f2
0000000 C o l u m n 1 sp sp sp sp C o l u m
0000020 n 2 nl s t r 1 sp sp sp sp sp sp sp 1 sp
0000040 sp sp sp sp sp nl s t r 2 sp sp sp sp sp sp
0000060 sp 2 sp sp sp sp sp sp nl s t r 3 sp sp sp
0000100 sp sp sp sp 3 sp sp sp sp sp sp nl
0000114
Transformar:
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2
Column2 Column1
1 str1
2 str2
3 str3
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2 | od -a
0000000 C o l u m n 2 sp C o l u m n 1 sp
0000020 sp sp nl 1 sp sp sp sp sp sp sp s t r 1 sp
0000040 sp sp sp sp sp nl 2 sp sp sp sp sp sp sp s t
0000060 r 2 sp sp sp sp sp sp nl 3 sp sp sp sp sp sp
0000100 sp s t r 3 sp sp sp sp sp sp nl
0000114
Por último, aunque el ejemplo de la pregunta no tiene cadenas de longitud desigual, esta expresión sed respalda este caso.
Expediente:
bash-3.2$ cat f3
Column1 Column2
str1 1
string2 2
str3 3
Transformar:
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3
Column2 Column1
1 str1
2 string2
3 str3
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3 | od -a
0000000 C o l u m n 2 sp C o l u m n 1 sp
0000020 sp sp nl 1 sp sp sp sp sp sp sp s t r 1 sp
0000040 sp sp sp sp sp nl 2 sp sp sp sp sp sp sp s t
0000060 r i n g 2 sp sp sp nl 3 sp sp sp sp sp sp
0000100 sp s t r 3 sp sp sp sp sp sp nl
0000114
Comparación con otros métodos de reordenamiento de columnas bajo shell
Sorprendentemente para una herramienta de manipulación de archivos, awk no es adecuado para cortar desde un campo hasta el final del registro. En sed esto se puede lograr utilizando expresiones regulares, por ejemplo, \(xxx.*$\)
dónde xxx
está la expresión para que coincida con la columna.
Usar pegar y cortar subshells se vuelve complicado cuando se implementan scripts de shell. El código que funciona desde la línea de comandos no se analiza cuando se introduce dentro de un script de shell. Al menos esta fue mi experiencia (lo que me llevó a este enfoque).
Ampliando la respuesta de @Met, también usando Perl:
Si la entrada y la salida están delimitadas por TAB:
perl -F'\t' -lane 'print join "\t", @F[1, 0]' in_file
Si la entrada y la salida están delimitadas por espacios en blanco:
perl -lane 'print join " ", @F[1, 0]' in_file
Aquí,
-e
le dice a Perl que busque el código en línea, en lugar de en un archivo de script separado,
-n
lee la línea de entrada 1 a la vez,
-l
elimina el separador de registro de entrada ( \n
en * NIX) después de leer la línea (similar a chomp
) y agrega salida separador de registros ( \n
en * NIX) para cada uno print
,
-a
divide la línea de entrada en el espacio en blanco en la matriz @F
,
-F'\t'
en combinación con -a
la línea de entrada en las TAB, en lugar del espacio en blanco en la matriz @F
.
@F[1, 0]
es la matriz compuesta de los elementos segundo y primero de la matriz @F
, en este orden. Recuerde que las matrices en Perl están indexadas a cero, mientras que los campos cut
están indexados a 1. Por lo tanto, los campos en @F[0, 1]
son los mismos campos que en cut -f1,2
.
Tenga en cuenta que dicha notación permite una manipulación más flexible de la entrada que en algunas otras respuestas publicadas anteriormente (que están bien para una tarea simple). Por ejemplo:
# reverses the order of fields:
perl -F'\t' -lane 'print join "\t", reverse @F' in_file
# prints last and first fields only:
perl -F'\t' -lane 'print join "\t", @F[-1, 0]' in_file
cut
que no admita este comando intuitivo de reordenamiento. De todos modos, otro consejo: se puede utilizarawk
's-FS
y-OFS
opciones para el uso de entrada personalizada y separadores de campo de salida (como-d
y--output-delimiter
paracut
).