Usando awk para imprimir todas las columnas desde el enésimo hasta el último

310

Esta línea funcionó hasta que tuve espacios en blanco en el segundo campo.

svn status | grep '\!' | gawk '{print $2;}' > removedProjs

¿Hay alguna manera de que awk imprima todo en $ 2 o más? ($ 3, $ 4 ... hasta que no tengamos más columnas?)

Supongo que debería agregar que estoy haciendo esto en un entorno Windows con Cygwin.

Andy
fuente
11
Por otro lado, el grep | awkes un antipatrón - quieresawk '/!/ { print $2 }'
tripleee
3
Unix "cortar" es más fácil ...svn status | grep '\!' | cut -d' ' -f2- > removedProjs
roblogic
Posible duplicado del resto de campos
acm
@tripleee: Estoy tan feliz de que hayas mencionado esto, ¡estoy frustrado de verlo en todas partes!
Graham Nicholls

Respuestas:

490

imprimirá todo menos la primera columna:

awk '{$1=""; print $0}' somefile

imprimirá todas menos dos primeras columnas:

awk '{$1=$2=""; print $0}' somefile
zed_0xff
fuente
93
gotcha: deja un espacio principal colgando sobre :(
raphinesse
55
Me gusta el enfoque pragmático. sin embargo, no es necesario usar cat, solo coloque el nombre del archivo después del comando awk
kon
45
@raphinesse se puede arreglar eso conawk '{$1=""; print substr($0,2)}' input_filename > output_filename
themiurgo
66
Esto no funciona con delimitadores que no sean espacios en blanco, los reemplaza con un espacio.
Dejan
3
Para delimitadores que no sean espacios en blanco, puede especificar el Separador de campo de salida (OFS), por ejemplo, a una coma: awk -F, -vOFS=, '{$1=""; print $0}'terminará con un delimitador inicial ( $1todavía se incluye, como una cadena vacía). Sin sedembargo, puede quitar eso con :awk -F, -vOFS=, '{$1=""; print $0}' | sed 's/^,//'
cherdt
99

Hay una pregunta duplicada con una respuesta más simple usando cut:

 svn status |  grep '\!' | cut -d\  -f2-

-despecifica el delimitador (espacio) , -fespecifica la lista de columnas (todas comenzando con la 2da)

Joshua Goldberg
fuente
También puede usar "-b" para especificar la posición (desde el enésimo carácter en adelante).
Dakatine
Como nota, aunque esto realiza la misma tarea que la awkversión, hay problemas de almacenamiento en línea con cut, que awkno tiene: stackoverflow.com/questions/14360640/…
sdaau
24
Agradable y simple, pero viene con una advertencia: awktrata varios caracteres espaciales adyacentes. como un solo separador, mientras cutque no; Además, aunque esto no es un problema en el caso en cuestión, cutsolo acepta un único carácter literal. como delimitador, mientras que awkpermite una expresión regular.
mklement0
Con base en esto: stackoverflow.com/a/39217130/8852408 , es probable que esta solución no sea muy eficiente.
FcknGioconda
85

Puede usar un bucle for para recorrer los campos de impresión de $ 2 a $ NF (variable incorporada que representa el número de campos en la línea).

Editar: Dado que "imprimir" agrega una nueva línea, querrás almacenar los resultados en el búfer:

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

Alternativamente, use printf:

awk '{for(i=2;i<=NF;i++){printf "%s ", $i}; printf "\n"}'
VeeArr
fuente
Así que intenté esto, pero creo que me falta algo ... esto es lo que hice svn status | grep '\!' El | gawk '{for (i = 1; i <= $ NF; i ++) print $ i "";}'> eliminadoProyectos
Andy
Dado que print agrega una nueva línea, querrás almacenar los resultados en el búfer. Mira mi edición.
VeeArr
1
Me gusta más esta respuesta porque muestra cómo recorrer los campos.
Edward Falk
3
Si desea que print use un espacio, cambie el separador de registro de salida: awk '{ORS = ""; for (i = 2; i <NF; i ++) print $ i} 'somefile
Christian Lescuyer
3
Siempre habrá algunos espacios demasiado. Esto funciona mejor: '{for(i=11;i<=NF-1;i++){printf "%s ", $i}; print $NF;}'sin espacios iniciales o finales.
Marki
24
awk '{out=$2; for(i=3;i<=NF;i++){out=out" "$i}; print out}'

Mi respuesta se basa en la de VeeArr , pero noté que comenzó con un espacio en blanco antes de imprimir la segunda columna (y el resto). Como solo tengo 1 punto de reputación, no puedo comentarlo, así que aquí va como una nueva respuesta:

comience con "out" como la segunda columna y luego agregue todas las otras columnas (si existen). Esto funciona bien siempre que haya una segunda columna.

Wim
fuente
2
Excelente, también eliminó el $ delante de la variable out, que también es importante.
Alexis Wilke
15

La mayoría de las soluciones con awk dejan un espacio. Las opciones aquí evitan ese problema.

Opción 1

Una solución de corte simple (funciona solo con delimitadores individuales):

command | cut -d' ' -f3-

opcion 2

Forzar una recalibración de awk a veces elimina el espacio inicial agregado (OFS) que queda al eliminar los primeros campos (funciona con algunas versiones de awk):

command | awk '{ $1=$2="";$0=$0;} NF=NF'

Opción 3

Imprimir cada campo formateado con printfdará más control:

$ in='    1    2  3     4   5   6 7     8  '
$ echo "$in"|awk -v n=2 '{ for(i=n+1;i<=NF;i++) printf("%s%s",$i,i==NF?RS:OFS);}'
3 4 5 6 7 8

Sin embargo, todas las respuestas anteriores cambian todos los FS repetidos entre campos a OFS. Construyamos un par de opciones que no hacen eso.

Opción 4 (recomendada)

Un bucle con sub para eliminar campos y delimitadores en el frente.
Y utilizando el valor de FS en lugar de espacio (que podría modificarse).
Es más portátil, y no provoca un cambio de FS a OFS: NOTA: El ^[FS]*es aceptar una entrada con espacios iniciales.

$ in='    1    2  3     4   5   6 7     8  '
$ echo "$in" | awk '{ n=2; a="^["FS"]*[^"FS"]+["FS"]+";
  for(i=1;i<=n;i++) sub( a , "" , $0 ) } 1 '
3     4   5   6 7     8

Opción 5

Es bastante posible construir una solución que no agregue espacios en blanco adicionales (iniciales o finales) y que conserve los espacios en blanco existentes utilizando la función gensubde GNU awk, ya que esto:

$ echo '    1    2  3     4   5   6 7     8  ' |
  awk -v n=2 'BEGIN{ a="^["FS"]*"; b="([^"FS"]+["FS"]+)"; c="{"n"}"; }
          { print(gensub(a""b""c,"",1)); }'
3     4   5   6 7     8 

También se puede usar para intercambiar un grupo de campos dado un recuento n:

$ echo '    1    2  3     4   5   6 7     8  ' |
  awk -v n=2 'BEGIN{ a="^["FS"]*"; b="([^"FS"]+["FS"]+)"; c="{"n"}"; }
          {
            d=gensub(a""b""c,"",1);
            e=gensub("^(.*)"d,"\\1",1,$0);
            print("|"d"|","!"e"!");
          }'
|3     4   5   6 7     8  | !    1    2  !

Por supuesto, en tal caso, el OFS se usa para separar ambas partes de la línea, y el espacio en blanco final de los campos todavía se imprime.

NOTA: [FS]* se utiliza para permitir espacios iniciales en la línea de entrada.

usuario 2350426
fuente
13

Personalmente probé todas las respuestas mencionadas anteriormente, pero la mayoría de ellas eran un poco complejas o simplemente no eran correctas. La forma más fácil de hacerlo desde mi punto de vista es:

awk -F" " '{ for (i=4; i<=NF; i++) print $i }'
  1. Donde -F "" define el delimitador para que awk lo use. En mi caso es el espacio en blanco, que también es el delimitador predeterminado para awk. Esto significa que -F "" puede ignorarse.

  2. Donde NF define el número total de campos / columnas. Por lo tanto, el bucle comenzará desde el cuarto campo hasta el último campo / columna.

  3. Donde $ N recupera el valor del enésimo campo. Por lo tanto, print $ i imprimirá el campo / columna actual en función del recuento de bucles.

koullislp
fuente
44
Problema, que imprime cada campo en una línea diferente.
mveroone
nada te detiene agregando esto al final :-) `| tr '\ n' '' '
koullislp
3
Un poco tarde pero awk '{for (i = 5; i <= NF; i ++) {printf "% s", $ i}}'
plitter
8
awk '{ for(i=3; i<=NF; ++i) printf $i""FS; print "" }'

lauhub propuso esta solución correcta, simple y rápida aquí

ajendrex
fuente
7

Esto me irritaba tanto, me senté y escribí un cutanalizador de especificación de campo similar, probado con GNU Awk 3.1.7.

Primero, cree un nuevo script de biblioteca Awk llamado pfcut, por ejemplo

sudo nano /usr/share/awk/pfcut

Luego, pegue el script a continuación y guárdelo. Después de eso, así es como se ve el uso:

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("-4"); }'
t1 t2 t3 t4

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("2-"); }'
t2 t3 t4 t5 t6 t7

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("-2,4,6-"); }'
t1 t2 t4 t6 t7

Para evitar escribir todo eso, supongo que lo mejor que se puede hacer (ver lo contrario ¿ Cargar automáticamente una función de usuario al inicio con awk? - Unix & Linux Stack Exchange ) es agregar un alias a ~/.bashrc; por ejemplo con:

$ echo "alias awk-pfcut='awk -f pfcut --source'" >> ~/.bashrc
$ source ~/.bashrc     # refresh bash aliases

... entonces puedes simplemente llamar:

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk-pfcut '/^/ { pfcut("-2,4,6-"); }'
t1 t2 t4 t6 t7

Aquí está la fuente del pfcutguión:

# pfcut - print fields like cut
#
# sdaau, GNU GPL
# Nov, 2013

function spfcut(formatstring)
{
  # parse format string
  numsplitscomma = split(formatstring, fsa, ",");
  numspecparts = 0;
  split("", parts); # clear/initialize array (for e.g. `tail` piping into `awk`)
  for(i=1;i<=numsplitscomma;i++) {
    commapart=fsa[i];
    numsplitsminus = split(fsa[i], cpa, "-");
    # assume here a range is always just two parts: "a-b"
    # also assume user has already sorted the ranges
    #print numsplitsminus, cpa[1], cpa[2]; # debug
    if(numsplitsminus==2) {
     if ((cpa[1]) == "") cpa[1] = 1;
     if ((cpa[2]) == "") cpa[2] = NF;
     for(j=cpa[1];j<=cpa[2];j++) {
       parts[numspecparts++] = j;
     }
    } else parts[numspecparts++] = commapart;
  }
  n=asort(parts); outs="";
  for(i=1;i<=n;i++) {
    outs = outs sprintf("%s%s", $parts[i], (i==n)?"":OFS); 
    #print(i, parts[i]); # debug
  }
  return outs;
}

function pfcut(formatstring) {
  print spfcut(formatstring);
}
sdaau
fuente
Parece que quieres usar cut, noawk
roblogic
5

Imprimir columnas a partir del n. ° 2 (la salida no tendrá espacio final al principio):

ls -l | awk '{sub(/[^ ]+ /, ""); print $0}'
savvadia
fuente
1
Agradable, aunque debe agregar +después del espacio, ya que los campos pueden estar separados por más de 1 espacio ( awktrata múltiples espacios adyacentes como un solo separador). Además, awkignorará los espacios iniciales, por lo que debe comenzar la expresión regular con ^[ ]*. Con el espacio como separador, incluso podría generalizar la solución; por ejemplo, lo siguiente devuelve todo desde el tercer campo: awk '{sub(/^[ ]*([^ ]+ +){2}/, ""); print $0}'sin embargo, se vuelve más complicado con separadores de campo arbitrarios.
mklement0
5

¿Funcionaría esto?

awk '{print substr($0,length($1)+1);}' < file

Sin embargo, deja algo de espacio en blanco en el frente.

Whaley
fuente
4
echo "1 2 3 4 5 6" | awk '{ $NF = ""; print $0}'

este usa awk para imprimir todo excepto el último campo

Kaushal Jha
fuente
3

Esto es lo que preferí de todas las recomendaciones:

Impresión de la 6ta a la última columna.

ls -lthr | awk '{out=$6; for(i=7;i<=NF;i++){out=out" "$i}; print out}'

o

ls -lthr | awk '{ORS=" "; for(i=6;i<=NF;i++) print $i;print "\n"}'
Manuel Parra
fuente
2

Si necesita columnas específicas impresas con delimitador arbitrario:

awk '{print $3 "  " $4}'

col # 3 col # 4

awk '{print $3 "anything" $4}'

col # 3anythingcol # 4

Entonces, si tiene espacios en blanco en una columna, serán dos columnas, pero puede conectarlo con cualquier delimitador o sin él.

I159
fuente
2

Solución Perl:

perl -lane 'splice @F,0,1; print join " ",@F' file

Se utilizan estas opciones de línea de comandos:

  • -n recorra 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 agrega nuevamente

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

  • -e ejecuta el código perl

splice @F,0,1 elimina limpiamente la columna 0 de la matriz @F

join " ",@F une los elementos de la matriz @F, utilizando un espacio entre cada elemento


Solución de Python:

python -c "import sys;[sys.stdout.write(' '.join(line.split()[1:]) + '\n') for line in sys.stdin]" < file

Chris Koknat
fuente
1

Si no desea volver a formatear la parte de la línea que no corta, la mejor solución que se me ocurre está escrita en mi respuesta en:

¿Cómo imprimir todas las columnas después de un número particular usando awk?

Corta lo que está antes del número de campo dado N, e imprime todo el resto de la línea, incluido el número de campo N y mantiene el espacio original (no formatea). No importa si la cadena del campo aparece también en otro lugar de la línea.

Definir una función:

fromField () { 
awk -v m="\x01" -v N="$1" '{$N=m$N; print substr($0,index($0,m)+1)}'
}

Y úsalo así:

$ echo "  bat   bi       iru   lau bost   " | fromField 3
iru   lau bost   
$ echo "  bat   bi       iru   lau bost   " | fromField 2
bi       iru   lau bost 

La salida mantiene todo, incluidos los espacios finales

En tu caso particular:

svn status | grep '\!' | fromField 2 > removedProjs

Si su archivo / secuencia no contiene caracteres de nueva línea en el medio de las líneas (podría estar usando un Separador de registro diferente), puede usar:

awk -v m="\x0a" -v N="3" '{$N=m$N ;print substr($0, index($0,m)+1)}'

El primer caso fallará solo en archivos / secuencias que contienen el raro carácter hexadecimal número 1

Robert Vila
fuente
0

Esto funcionaría si está usando Bash y podría usar tantas 'x' como elementos que desee descartar e ignora múltiples espacios si no se escapan.

while read x b; do echo "$b"; done < filename
Stuart Rothrock
fuente
0

Perl:

@m=`ls -ltr dir | grep ^d | awk '{print \$6,\$7,\$8,\$9}'`;
foreach $i (@m)
{
        print "$i\n";

}
pkm
fuente
1
Esto no responde la pregunta, que generaliza el requisito de imprimir desde la enésima columna hasta el final .
roaima
0

Esta awkfunción devuelve una subcadena $0que incluye campos de begina end:

function fields(begin, end,    b, e, p, i) {
    b = 0; e = 0; p = 0;
    for (i = 1; i <= NF; ++i) {
        if (begin == i) { b = p; }
        p += length($i);
        e = p;
        if (end == i) { break; }
        p += length(FS);
    }
    return substr($0, b + 1, e - b);
}

Para obtener todo a partir del campo 3:

tail = fields(3);

Para obtener una sección $0que cubre los campos 3 a 5:

middle = fields(3, 5);

b, e, p, isin sentido en la lista de parámetros de función es solo una awkforma de declarar variables locales.

wonder.mice
fuente
0

Quiero extender las respuestas propuestas a la situación en la que los campos están delimitados posiblemente por varios espacios en blanco , cutsupongo que la razón por la cual el OP no está utilizando .

Sé que el OP preguntó awk, pero un sedenfoque funcionaría aquí (ejemplo con columnas de impresión del 5 al último):

  • enfoque de sed puro

    sed -r 's/^\s*(\S+\s+){4}//' somefile

    Explicación:

    • s/// se usa la forma estándar para realizar la sustitución
    • ^\s* coincide con cualquier espacio en blanco consecutivo al comienzo de la línea
    • \S+\s+ significa una columna de datos (caracteres que no son espacios en blanco seguidos de caracteres en espacios en blanco)
    • (){4} significa que el patrón se repite 4 veces.
  • sed y corte

    sed -r 's/^\s+//; s/\s+/\t/g' somefile | cut -f5-

    simplemente reemplazando espacios en blanco consecutivos por una sola pestaña;

  • tr y corte: trtambién se puede usar para exprimir caracteres consecutivos con la -sopción.

    tr -s [:blank:] <somefile | cut -d' ' -f5-
PlasmaBinturong
fuente
-1

Los ejemplos de Awk parecen complejos aquí, aquí hay una sintaxis simple de shell Bash:

command | while read -a cols; do echo ${cols[@]:1}; done

Donde 1es su n º conteo columna desde 0.


Ejemplo

Dado este contenido de archivo ( in.txt):

c1
c1 c2
c1 c2 c3
c1 c2 c3 c4
c1 c2 c3 c4 c5

Aquí está la salida:

$ while read -a cols; do echo ${cols[@]:1}; done < in.txt 

c2
c2 c3
c2 c3 c4
c2 c3 c4 c5
kenorb
fuente
-1

No estaba contento con ninguna de las awksoluciones presentadas aquí porque quería extraer las primeras columnas y luego imprimir el resto, así que recurrí a ellas perl. El siguiente código extrae las dos primeras columnas y muestra el resto tal como está:

echo -e "a  b  c  d\te\t\tf g" | \
  perl -ne 'my @f = split /\s+/, $_, 3; printf "first: %s second: %s rest: %s", @f;'

La ventaja en comparación con la perlsolución de Chris Koknat es que realmente solo los primeros n elementos se separan de la cadena de entrada; el resto de la cadena no se divide en absoluto y, por lo tanto, permanece completamente intacto. Mi ejemplo demuestra esto con una mezcla de espacios y pestañas.

Para cambiar la cantidad de columnas que se deben extraer, reemplace 3en el ejemplo con n + 1.

Martin von Wittich
fuente
-1
ls -la | awk '{o=$1" "$3; for (i=5; i<=NF; i++) o=o" "$i; print o }'

De esta respuesta no está mal, pero el espacio natural se ha ido.
Por favor, compárelo con este:

ls -la | cut -d\  -f4-

Entonces verías la diferencia.

Incluso ls -la | awk '{$1=$2=""; print}'que se basa en la respuesta votada mejor hasta ahora no es preservar el formato.

Por lo tanto, usaría lo siguiente, y también permite columnas selectivas explícitas al principio:

ls -la | cut -d\  -f1,4-

Tenga en cuenta que cada espacio también cuenta para las columnas, por lo que, por ejemplo, a continuación, las columnas 1 y 3 están vacías, 2 es INFO y 4 es:

$ echo " INFO  2014-10-11 10:16:19  main " | cut -d\  -f1,3

$ echo " INFO  2014-10-11 10:16:19  main " | cut -d\  -f2,4
INFO 2014-10-11
$
Arntg
fuente
-1

Si desea texto formateado, encadene sus comandos con echo y use $ 0 para imprimir el último campo.

Ejemplo:

for i in {8..11}; do
   s1="$i"
   s2="str$i"
   s3="str with spaces $i"
   echo -n "$s1 $s2" | awk '{printf "|%3d|%6s",$1,$2}'
   echo -en "$s3" | awk '{printf "|%-19s|\n", $0}'
done

Huellas dactilares:

|  8|  str8|str with spaces 8  |
|  9|  str9|str with spaces 9  |
| 10| str10|str with spaces 10 |
| 11| str11|str with spaces 11 |
sintaxis
fuente
-9

Debido a una respuesta equivocada con más votos con 340 votos, ¡acabo de perder 5 minutos de mi vida! ¿Alguien intentó esta respuesta antes de votar esto? Aparentemente no. Completamente inutil.

Tengo un registro donde después de $ 5 con una dirección IP puede haber más texto o no texto. Necesito todo, desde la dirección IP hasta el final de la línea, si hubiera algo después de $ 5. En mi caso, esto es realmente dentro de un programa awk, no un awk oneliner, por lo que awk debe resolver el problema. Cuando trato de eliminar los primeros 4 campos usando la respuesta más votada pero completamente incorrecta:

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218" | awk '{$1=$2=$3=$4=""; printf "[%s]\n", $0}'

escupe una respuesta incorrecta e inútil (agregué [..] para demostrar):

[    37.244.182.218 one two three]

Incluso hay algunas sugerencias para combinar substr con esta respuesta incorrecta. Como esa complicación es una mejora.

En cambio, si las columnas tienen un ancho fijo hasta que se necesita el punto de corte y awk, la respuesta correcta es:

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218" | awk '{printf "[%s]\n", substr($0,28)}'

que produce la salida deseada:

[37.244.182.218 one two three]
Pila
fuente