Imprimiendo todo excepto el primer campo con awk

108

Tengo un archivo que se parece a esto:

AE  United Arab Emirates
AG  Antigua & Barbuda
AN  Netherlands Antilles
AS  American Samoa
BA  Bosnia and Herzegovina
BF  Burkina Faso
BN  Brunei Darussalam

Y me gustaría invertir el orden, imprimiendo primero todo excepto $ 1 y luego $ 1:

United Arab Emirates AE

¿Cómo puedo hacer el truco "todo excepto el campo 1"?

cfischer
fuente
2
Hola @cfisher, se puede hacer sin un ángulo de bucle sin el espacio adicional.
Juan Diego Godoy Robles

Respuestas:

91

Asignando $1obras pero dejará un espacio de protagonismo:awk '{first = $1; $1 = ""; print $0, first; }'

También puede encontrar el número de columnas NFy usarlo en un ciclo.

Ben Jackson
fuente
2
Para los totalmente perezosos; aquí está el código klashxx .
Serge Stroobandt
1
Excelente. Se deshizo del espacio principal con sed: awk {'first = $1; $1=""; print $0'}|sed 's/^ //g'
Thyag
El espacio se elimina fácilmente con VIM presionando 'Ctrl + V Gd' en modo normal
Santi
107

$1=""deja un espacio como lo mencionó Ben Jackson, así que use un forbucle:

awk '{for (i=2; i<=NF; i++) print $i}' filename

Entonces, si su cadena fue "uno dos tres", la salida será:

dos
tres

Si desea el resultado en una fila, puede hacer lo siguiente:

awk '{for (i=2; i<NF; i++) printf $i " "; print $NF}' filename

Esto le dará: "dos tres"

7winkie
fuente
4
y un espacio adicional al final
NeronLeVelu
2
mejor usar: awk '{for(i=2;i<=NF;i++){ printf("%s",( (i>2) ? OFS : "" ) $i) } ; print ;}' que: imprime los campos 2 a NF, agrega el Separador de campos de salida según sea necesario (es decir, excepto antes de $ 2). La última impresión agrega una nueva línea final para finalizar la impresión de la línea actual. Ese funcionará si cambia FS / OFS (es decir, no siempre será "espacio")
Olivier Dulac
El segundo funcionó muy bien para mí. El primero, no tanto. No estoy seguro de por qué. Cortó todo el texto.
voces
72

Utilice el cutcomando con la --complementopción:

$ echo a b c | cut -f 1 -d ' '
a
$ echo a b c | cut -f 1,2 -d ' '
a b
$ echo a b c | cut -f 1 -d ' ' --complement
b c
zeleniy
fuente
2
Si bien no respondí la pregunta específica de awk, encontré esto más útil ya que awk estaba eliminando espacios duplicados, y cut no lo hace.
Fmstrat
19
echo a b c | cut -d' ' -f 2- es una alternativa
Luis
2
Agradable - La solución @Luis funciona en Mac, que no admite --complemento
metadaddy
21

Quizás la forma más concisa:

$ awk '{$(NF+1)=$1;$1=""}sub(FS,"")' infile
United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Explicación:

$(NF+1)=$1: Generador de un último campo "nuevo".

$1="": Establece el primer campo original en nulo

sub(FS,""): Después de las dos primeras acciones, {$(NF+1)=$1;$1=""}elimine el primer separador de campo usando sub. La impresión final está implícita.

Juan Diego Godoy Robles
fuente
13
awk '{sub($1 FS,"")}7' YourFile

Elimine el primer campo y el separador, e imprima el resultado ( 7es un valor distinto de cero, por lo que imprime $ 0).

NeronLeVelu
fuente
¡La mejor respuesta! Voto a favor. ¿En qué se diferencia de solo usar 1? Me pregunto el uso de este patrón y quería entenderlo. ¡Gracias!
Abhijeet Rastogi
10
awk '{ saved = $1; $1 = ""; print substr($0, 2), saved }'

Establecer el primer campo en ""deja una única copia de OFSal comienzo de $0. Suponiendo que OFSes solo un carácter (por defecto, es un solo espacio), podemos eliminarlo con substr($0, 2). Luego agregamos la copia guardada de $1.

dudoso
fuente
6

Si está abierto a una solución de Perl ...

perl -lane 'print join " ",@F[1..$#F,0]' file

es una solución simple con un separador de entrada / salida de un espacio, que produce:

United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Este siguiente es un poco más complejo.

perl -F`  ` -lane 'print join "  ",@F[1..$#F,0]' file

y asume que el separador de entrada / salida tiene dos espacios:

United Arab Emirates  AE
Antigua & Barbuda  AG
Netherlands Antilles  AN
American Samoa  AS
Bosnia and Herzegovina  BA
Burkina Faso  BF
Brunei Darussalam  BN

Se utilizan estas opciones de la línea de comandos:

  • -n bucle alrededor de cada línea del archivo de entrada, no imprima automáticamente cada línea

  • -l elimina las nuevas líneas antes del procesamiento y las vuelve a agregar después

  • -amodo de división automática: divide las líneas de entrada en la matriz @F. Predeterminado para dividir en espacios en blanco

  • -F modificador autosplit, en este ejemplo se divide en '' (dos espacios)

  • -e ejecutar el siguiente código de Perl

@Fes la matriz de palabras en cada línea, indexada comenzando con 0
$#Fes el número de palabras en @F
@F[1..$#F]es una parte de la matriz del elemento 1 hasta el último elemento
@F[1..$#F,0]es una parte de la matriz del elemento 1 hasta el último elemento más el elemento 0

Chris Koknat
fuente
1
Lo ejecuté y tenía un número adicional al final, así que usé esta versión: perl -lane 'shift @F; imprimir unirse "", @F '
Hans Poo
2

El separador de campo en gawk (al menos) puede ser una cadena y un carácter (también puede ser una expresión regular). Si sus datos son consistentes, esto funcionará:

awk -F "  " '{print $2,$1}' inputfile

Son dos espacios entre las comillas dobles.

Pausado hasta nuevo aviso.
fuente
La mejor respuesta para la situación actual, pero, técnicamente, esto no responde a la pregunta de cómo imprimir todo excepto el primer campo.
Dan Moulding
@DanMoulding: Siempre y cuando el archivo es consistente en el uso de dos espacios para separar el código de país y no hay otras apariciones de dos espacios juntos, mi respuesta hace la pregunta de direcciones.
Pausado hasta nuevo aviso.
2
Las personas que llegan a esta pregunta llegan aquí porque quieren saber cómo imprimir todo menos el primer campo (vea el título de la pregunta). Así es como aterricé aquí. Su respuesta muestra cómo imprimir el primer campo seguido del segundo campo. Si bien esta es probablemente la mejor solución para la situación particular del OP, no resuelve el problema general de cómo imprimir todo excepto el primer campo.
Dan Moulding
2

awk '{ tmp = $1; sub(/^[^ ]+ +/, ""); print $0, tmp }'

Arkku
fuente
2

Muevamos todos los registros al siguiente y establezcamos el último como el primero:

$ awk '{a=$1; for (i=2; i<=NF; i++) $(i-1)=$i; $NF=a}1' file
United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Explicación

  • a=$1 guarde el primer valor en una variable temporal.
  • for (i=2; i<=NF; i++) $(i-1)=$i guarde el valor del campo N en el campo (N-1).
  • $NF=aguarde el primer valor ( $1) en el último campo.
  • {}1verdadera condición para hacer awkque la acción predeterminada: {print $0}.

De esta manera, si tiene otro separador de campo, el resultado también es bueno:

$ cat c
AE-United-Arab-Emirates
AG-Antigua-&-Barbuda
AN-Netherlands-Antilles
AS-American-Samoa
BA-Bosnia-and-Herzegovina
BF-Burkina-Faso
BN-Brunei-Darussalam

$ awk 'BEGIN{OFS=FS="-"}{a=$1; for (i=2; i<=NF; i++) $(i-1)=$i; $NF=a}1' c
United-Arab-Emirates-AE
Antigua-&-Barbuda-AG
Netherlands-Antilles-AN
American-Samoa-AS
Bosnia-and-Herzegovina-BA
Burkina-Faso-BF
Brunei-Darussalam-BN
fedorqui 'Así que deja de hacer daño'
fuente
1

Un primer intento parece funcionar para su caso particular.

awk '{ f = $1; i = $NF; while (i <= 0); gsub(/^[A-Z][A-Z][ ][ ]/,""); print $i, f; }'
Wesley Rice
fuente
1

Opción 1

Existe una solución que funciona con algunas versiones de awk:

awk '{ $(NF+1)=$1;$1="";$0=$0;} NF=NF ' infile.txt

Explicación:

       $(NF+1)=$1                          # add a new field equal to field 1.
                  $1=""                    # erase the contents of field 1.
                        $0=$0;} NF=NF      # force a re-calc of fields.
                                           # and use NF to promote a print.

Resultado:

United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Sin embargo, eso podría fallar con versiones anteriores de awk.


opcion 2

awk '{ $(NF+1)=$1;$1="";sub(OFS,"");}1' infile.txt

Es decir:

awk '{                                      # call awk.
       $(NF+1)=$1;                          # Add one trailing field.
                  $1="";                    # Erase first field.
                        sub(OFS,"");        # remove leading OFS.
                                    }1'     # print the line.

Tenga en cuenta que lo que debe borrarse es el OFS, no el FS. La línea se vuelve a calcular cuando se asigna el campo $ 1. Eso cambia todas las ejecuciones de FS a una OFS.


Pero incluso esa opción aún falla con varios delimitadores, como se muestra claramente al cambiar el OFS:

awk -v OFS=';' '{ $(NF+1)=$1;$1="";sub(OFS,"");}1' infile.txt

Esa línea dará como resultado:

United;Arab;Emirates;AE
Antigua;&;Barbuda;AG
Netherlands;Antilles;AN
American;Samoa;AS
Bosnia;and;Herzegovina;BA
Burkina;Faso;BF
Brunei;Darussalam;BN

Eso revela que las ejecuciones de FS se están cambiando a una OFS.
La única forma de evitarlo es evitar el recálculo del campo.
Una función que puede evitar volver a calcular es sub.
El primer campo podría capturarse, luego eliminarse de $ 0 con sub, y luego volver a imprimir ambos.

Opción 3

awk '{ a=$1;sub("[^"FS"]+["FS"]+",""); print $0, a;}' infile.txt
       a=$1                                   # capture first field.
       sub( "                                 # replace: 
             [^"FS"]+                         # A run of non-FS
                     ["FS"]+                  # followed by a run of FS.
                            " , ""            # for nothing.
                                  )           # Default to $0 (the whole line.
       print $0, a                   # Print in reverse order, with OFS.


United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Incluso si cambiamos el FS, el OFS y / o agregamos más delimitadores, funciona.
Si el archivo de entrada se cambia a:

AE..United....Arab....Emirates
AG..Antigua....&...Barbuda
AN..Netherlands...Antilles
AS..American...Samoa
BA..Bosnia...and...Herzegovina
BF..Burkina...Faso
BN..Brunei...Darussalam

Y el comando cambia a:

awk -vFS='.' -vOFS=';' '{a=$1;sub("[^"FS"]+["FS"]+",""); print $0,a;}' infile.txt

La salida será (aún conservando los delimitadores):

United....Arab....Emirates;AE
Antigua....&...Barbuda;AG
Netherlands...Antilles;AN
American...Samoa;AS
Bosnia...and...Herzegovina;BA
Burkina...Faso;BF
Brunei...Darussalam;BN

El comando podría expandirse a varios campos, pero solo con awks modernos y con la opción --re-interval activa. Este comando en el archivo original:

awk -vn=2 '{a=$1;b=$2;sub("([^"FS"]+["FS"]+){"n"}","");print $0,a,b;}' infile.txt

Producirá esto:

Arab Emirates AE United
& Barbuda AG Antigua
Antilles AN Netherlands
Samoa AS American
and Herzegovina BA Bosnia
Faso BF Burkina
Darussalam BN Brunei
Comunidad
fuente
1

Si está abierto a otra solución de Perl:

perl -ple 's/^(\S+)\s+(.*)/$2 $1/' file
Kjetil S.
fuente
0

También hay una opción sed ...

 sed 's/\([^ ]*\)  \(.*\)/\2 \1/' inputfile.txt

Explicado...

Swap
\([^ ]*\) = Match anything until we reach a space, store in $1
\(.*\)    = Match everything else, store in $2
With
\2        = Retrieve $2
\1        = Retrieve $1

Explicado más a fondo ...

s    = Swap
/    = Beginning of source pattern
\(   = start storing this value
[^ ] = text not matching the space character
*    = 0 or more of the previous pattern
\)   = stop storing this value
\(   = start storing this value
.    = any character
*    = 0 or more of the previous pattern
\)   = stop storing this value
/    = End of source pattern, beginning of replacement
\2   = Retrieve the 2nd stored value
\1   = Retrieve the 1st stored value
/    = end of replacement
ZeBadger
fuente
0

Otra forma más ...

... esto vuelve a unir los campos 2 a NF con el FS y genera una línea por línea de entrada

awk '{for (i=2;i<=NF;i++){printf $i; if (i < NF) {printf FS};}printf RS}'

Utilizo esto con git para ver qué archivos se han modificado en mi directorio de trabajo:

git diff| \
    grep '\-\-git'| \
    awk '{print$NF}'| \
    awk -F"/" '{for (i=2;i<=NF;i++){printf $i; if (i < NF) {printf FS};}printf RS}'
Rondó
fuente
-3

Otra forma fácil de usar el comando cat

cat filename | awk '{print $2,$3,$4,$5,$6,$1}' > newfilename
Escorpión
fuente
Voté en contra porque este no es un enfoque dinámico. Con esto, necesita saber la cantidad de argumentos y asumir que sus datos son consistentes. Los datos casi nunca son consistentes y su enfoque debe tener esto en cuenta la mayor parte del tiempo.
xh3b4sd