Uso de awk para imprimir todas las columnas desde la última hasta la última

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 de Windows con Cygwin.

imprimirá todo menos la primera columna:

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

imprimirá todas menos dos primeras columnas:

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

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

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

-d especifica el delimeter (espacio) , -f especifica la lista de columnas (todas comienzan con el 2nd)

Puede usar un for-loop 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: ya que “imprimir” agrega una nueva línea, querrá almacenar en búfer los resultados:

 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"}' 
 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 rest). Como solo tengo 1 punto de reputación, no puedo comentar sobre él, 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 va bien siempre que haya una segunda columna.

Personalmente probé todas las respuestas mencionadas anteriormente, pero la mayoría 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 usar awk. 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 ciclo comenzará desde el 4º campo hasta el último campo / columna.

  3. Donde $ N recupera el valor del campo Nth. Por lo tanto, print $ i imprimirá el campo / columna actual basándose en el recuento de bucles.

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

Obligar a un awk a recalcular algunas veces elimina el espacio inicial añadido (OFS) que queda eliminando 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 printf dará más control:

 $ in=' 1 2 3 4 5 6 7 8 ' $ echo "$in"|awk -vn=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 hagan eso.

Opción 4 (recomendado)

Un bucle con sub para eliminar campos y delimitadores en el frente.
Y usando el valor de FS en lugar de espacio (que podría cambiarse).
Es más portátil y no desencadena un cambio de FS a OFS: NOTA: El ^[FS]* es para 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 preserve los espacios en blanco existentes utilizando la función gensub de GNU awk, como esta:

 $ echo ' 1 2 3 4 5 6 7 8 ' | awk -vn=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 -vn=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 posterior de los campos todavía se imprime.

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

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

Primero, cree una nueva secuencia de comandos de la biblioteca Awk llamada pfcut , con, 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, creo 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 ej. con:

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

… entonces solo puedes llamar:

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

Aquí está la fuente de la pfcut comandos pfcut :

 # 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 eg `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: "ab" # 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); } 

Imprimir columnas comenzando desde el n. ° 2 (la salida no tendrá un espacio al final):

 ls -l | awk '{sub(/[^ ]+ /, ""); print $0}' 

¿Esto funcionaría?

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

Aunque deja algunos espacios en blanco en el frente.

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

este usa awk para imprimir todo, excepto el último campo

Esto es lo que preferí de todas las recomendaciones:

Imprimir desde la 6ª 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"}' 
 awk '{ for(i=3; i< =NF; ++i) printf $i""FS; print "" }' 

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

Si necesita columnas específicas impresas con delímetro 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.

Solución Perl:

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

Estas opciones de línea de comando se usan:

  • -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 agrega de nuevo en adelante

  • -a modo autosplit – divide líneas de entrada en la matriz @F. Se predetermina a la división 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, usando 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

Esto funcionaría si está utilizando Bash y podría usar tantos ‘x’ como elementos que desea descartar e ignora múltiples espacios si no se escapan.

 while read xb; do echo "$b"; done < filename 

Si no quiere volver a formatear la parte de la línea que no corta, la mejor solución que puedo pensar está escrita en mi respuesta en:

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

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

Definir una función:

 fromField () { awk -vm="\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 lo mantiene todo, incluidos los espacios finales

En tu caso particular:

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

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

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

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

Perl:

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

Esta función awk devuelve una subcadena de $0 que incluye campos de begin a 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 comenzando desde el campo 3:

 tail = fields(3); 

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

 middle = fields(3, 5); 

b, e, p, i sinsentido en la lista de parámetros de función es solo una forma awk de declarar variables locales.

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

Sé que OP preguntó acerca de awk , pero un enfoque sed podría funcionar aquí (ejemplo con columnas de impresión desde la quinta hasta la última):

  • enfoque de sed puro

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

    Explicación:

    • s/// se usa de la manera 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 no blancos, seguidos de caracteres 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 cut: tr también se puede utilizar para exprimir caracteres consecutivos con la opción -s .

     tr -s [:blank:]  

Los ejemplos de Awk se ven complejos aquí, aquí hay una syntax simple de Bash shell:

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

Donde 1 es tu nésima columna contando 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á el resultado:

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

No estaba contento con ninguna de las soluciones awk presentadas aquí porque quería extraer las primeras columnas y luego imprimir el rest, por lo que perl a perl . El siguiente código extrae las dos primeras columnas y muestra el rest tal como está:

 echo -e "abcd\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 solución perl de Chris Koknat es que realmente solo los primeros n elementos se separan de la cadena de entrada; el rest de la cuerda no está dividida en absoluto y, por lo tanto, se mantiene completamente intacta. Mi ejemplo lo demuestra con una combinación de espacios y tabs.

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

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

a partir de esta respuesta no está mal, pero el espaciado natural se ha ido.
Por favor, compáralo con este:

 ls -la | cut -d\ -f4- 

Entonces verías la diferencia.

Incluso ls -la | awk '{$1=$2=""; print}' ls -la | awk '{$1=$2=""; print}' ls -la | awk '{$1=$2=""; print}' que se basa en la respuesta que mejor se votó hasta el momento, no conserva 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 cuenta para las columnas también, así que por ejemplo en el siguiente, 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 $ 

Si desea texto formateado, encadene sus comandos con eco 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 | 

¡Debido a una carta más mal votada con 340 votos, perdí 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 actualmente un progtwig awk, no un awk oneliner, por lo que awk debe resolver el problema. Cuando bash 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. Al igual que esa complicación es una mejora.

En cambio, si las columnas son de ancho fijo hasta que se necesite 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 el resultado deseado:

 [37.244.182.218 one two three]