2011-07-08 11 views

Respuesta

5

Sí, esto se puede hacer muy bien en awk. Es fácil obtener todos los campos sin ningún tipo de ataques serios.

(Este ejemplo funciona en ambas The One True Awk y en gawk.)

{ 
    split($0, a, "\"") 
    $2 = a[2] 
    $3 = $(NF - 1) 
    $4 = $NF 
    print "and the fields are ", $1, "+", $2, "+", $3, "+", $4 
} 
+0

Para formatear para un trazador de líneas: 'cat data.txt | awk 'split ($ 0, a, "\" ") {$ 2 = a [2]} {$ 3 = $ (NF - 1)} {$ 4 = $ NF} {imprimir" y los campos son ", $ 1," + ", $ 2," + ", $ 3," + ", $ 4} '' –

+7

Esto solo funciona si tiene un solo campo cotizado, en la segunda posición, y tiene 4 campos en total. No es genérico. Una solución donde aceptará cualquier campo citado en cualquier posición será ideal. –

4

Prueba esto:

$ cat data.txt | awk -F\" '{print $2}' 
I am ABC 
I am not ABC 
+0

Debo señalar que esto no es particularmente genérico - es simplemente cambia el separador de campo a '" y selecciona el segundo campo. –

+0

Pero si quiero usar la información antes y después ... no funcionará = ( –

+0

@Roy Chan - cierto. Awk no es realmente el correcto herramienta para analizar cadenas entre comillas. Vaya a la tercera publicación [en este vínculo de Caché de Google horriblemente formateado] (http://webcache.googleusercontent.com/search?q=cache:HA9Ix2yPEasJ:forums11.itrc.hp.com/service/forums/questionanswer.do%) 3FthreadId% 3D1028610 + awk + quotes + field & cd = 1 & hl = en & ct = clnk & gl = us & client = safari & source = www.google.com) y puede ver un ejemplo que es mucho más largo pero podría ayudar. –

0

bien, si realmente quiere los tres campos, se puede conseguir, pero se necesita una gran cantidad de tuberías:

$ cat data.txt | awk -F\" '{print $1 "," $2 "," $3}' | awk -F' ,' '{print $1 "," $2}' | awk -F', ' '{print $1 "," $2}' | awk -F, '{print $1 "," $2 "," $3}' 
ABC,I am ABC,35 
DEF,I am not ABC,42 

En la última tubería, tienes los tres campos para hacer lo que quieras.

+0

En realidad, hay 4 campos. – DigitalRoss

+0

Oops - Me perdí en el envío original. –

2

He arrugado una función que vuelve a dividir $ 0 en una matriz llamada B. Los espacios entre comillas dobles no actúan como separadores de campo. Funciona con cualquier cantidad de campos, una combinación de entre comillas y sin comillas. Aquí va:

#!/usr/bin/gawk -f 

# Resplit $0 into array B. Spaces between double quotes are not separators. 
# Single quotes not handled. No escaping of double quotes. 
function resplit(  a, l, i, j, b, k, BNF) # all are local variables 
{ 
    l=split($0, a, "\"") 
    BNF=0 
    delete B 
    for (i=1;i<=l;++i) 
    { 
    if (i % 2) 
    { 
     k=split(a[i], b) 
     for (j=1;j<=k;++j) 
     B[++BNF] = b[j] 
    } 
    else 
    { 
     B[++BNF] = "\""a[i]"\"" 
    } 
    } 
} 

{ 
    resplit() 

    for (i=1;i<=length(B);++i) 
    print i ": " B[i] 
} 

espero que ayude.

2

La respuesta superior para esta pregunta solo funciona para líneas con un solo campo entre comillas. Cuando encontré esta pregunta, necesitaba algo que pudiera funcionar para una cantidad arbitraria de campos cotizados.

Finalmente me encontré con an answer by Wintermute in another thread, y proporcionó una buena solución generalizada para este problema. Lo acabo de modificar para eliminar las comillas. Tenga en cuenta que necesita invocar awk con -F\" cuando ejecuta el siguiente programa.

BEGIN { OFS = "" } { 
    for (i = 1; i <= NF; i += 2) { 
     gsub(/[ \t]+/, ",", $i) 
    } 
    print 
} 

Esto funciona mediante la observación de que todos los demás elementos de la matriz estará dentro de las comillas cuando se separan por el "-character, y de forma que sustituye el espacio en blanco que divide los que no entre comillas con una coma.

continuación, se pueden encadenar fácilmente otra instancia de awk para hacer lo que necesita procesamiento (sólo tiene que utilizar el interruptor separador de campo de nuevo, -F,)

tenga en cuenta que esto podría romperse si se cita el primer campo -. yo no tengo probado. Si lo hace, sin embargo, debería ser fácil de arreglar agregando una instrucción if para comenzar en 2 rath mayor que 1 si el primer carácter de la línea es un ".

0

Aquí hay algo así como lo que finalmente obtuve trabajando que es más genérico para mi proyecto. Tenga en cuenta que no utiliza awk.

someText="ABC \"I am ABC\" 35 DESC '1 23' testing 456" 
putItemsInLines() { 
    local items="" 
    local firstItem="true" 
    while test $# -gt 0; do 
     if [ "$firstItem" == "true" ]; then 
      items="$1" 
      firstItem="false" 
     else 
      items="$items 
$1" 
     fi 
     shift 
    done 
    echo "$items" 
} 

count=0 
while read -r valueLine; do 
    echo "$count: $valueLine" 
    count=$(($count + 1)) 
done <<< "$(eval putItemsInLines $someText)" 

que da salida:

0: ABC 
1: I am ABC 
2: 35 
3: DESC 
4: 1 23 
5: testing 
6: 456 
3

Otra alternativa sería el uso de la variable de FPAT, que define una expresión regular que describe el contenido de cada campo.

Guardar este script AWK como parse.awk:

#!/bin/awk -f 

BEGIN { 
    FPAT = "([^ ]+)|(\"[^\"]+\")" 
} 
{ 
    print $2 
} 

hacerlo ejecutable con chmod +x ./parse.awk de analizar su archivo de datos como ./parse.awk data.txt:

"I am ABC" 
"I am not ABC" 
+0

Gracias por la expresión regular! ;-) Me salvó al menos 20 minutos de frustración intentos. +1 – jweyrich

+0

Esta debería ser la respuesta aceptada. Funciona como un amuleto, gracias. – Nico

Cuestiones relacionadas