GNU/Linux >> Tutoriales Linux >  >> Linux

¿Cómo extraer/cambiar líneas en un archivo de texto cuyos datos están separados en campos?

¿Cómo puedo manipular datos basados ​​en campos desde la línea de comandos? Por ejemplo

  • ¿Cómo puedo imprimir solo líneas cuyo campo Nth es foo? ?
  • ¿Cómo puedo imprimir solo líneas cuyo campo N no es foo? ?
  • ¿Cómo puedo imprimir solo líneas cuyo campo N coincide con foo? ?
  • ¿Cómo puedo cambiar el campo N a foo? ?

¿Existe un enfoque estándar o un conjunto de herramientas que facilite la manipulación de datos de campo en sistemas *nix?

Respuesta aceptada:

Hay dos enfoques básicos que se pueden usar cuando se trata de campos:i) usar una herramienta que comprenda los campos; ii) utilizar una expresión regular. De los dos, el primero suele ser más robusto y más simple.

Muchas de las herramientas comúnmente disponibles en *nix están diseñadas explícitamente para manejar campos o tienen ingeniosos trucos para facilitarlo.

1. Use una herramienta que comprenda los campos

1.1 mal

La herramienta clásica aquí es awk . Dividirá automáticamente cada línea de entrada en campos (el separador de campo es un espacio en blanco de forma predeterminada, pero se puede cambiar usando -F bandera) y los campos están disponibles para el awk secuencia de comandos como $n donde n es el número de campo. El primer campo es $1 , el segundo $2 etc.

  • Imprime líneas cuyo tercer campo es foo .

    awk '$3=="foo"' file
    

    Cambiando el delimitador a :

    awk -F":" '$3=="foo"' file
    

    La acción predeterminada de awk es imprimir. Por lo tanto, los comandos anteriores imprimirán todas las líneas cuyo tercer campo sea foo . Al usar -F , puede establecer separadores de campo arbitrarios e incluso utilizar expresiones regulares.

  • ¿Cómo puedo imprimir solo líneas cuyo tercer campo no sea foo? ?

    awk '$3!="foo"' file
    
  • ¿Cómo puedo imprimir solo líneas cuyo tercer campo coincida con foo? ?

    Si solo está buscando campos que coincidan con un patrón (por ejemplo, foo coincide con foobar ), utilice ~ en lugar de == :

    awk '$3~/foo/' file
    
  • ¿Cómo puedo imprimir solo líneas cuyo tercer campo no coincide con foo? ?

    awk '$3!~/foo/' file
    
  • ¿Cómo puedo cambiar el tercer campo a foo? ?

    awk '$3="foo"' file
    

1.2Perl

Otra opción es perl de una sola línea. Al igual que awk, Perl es un lenguaje de secuencias de comandos con todas las funciones, pero también se puede ejecutar como un programa de línea de comandos tomando una secuencia de comandos como entrada. Su comportamiento se modifica mediante cambios en la línea de comandos, los más relevantes para esta pregunta son:

  • -e :el script que perl debería ejecutarse;
  • -n :lee el archivo de entrada línea por línea;
  • -p :imprime cada línea de entrada después de aplicar el script proporcionado por -e;
  • -l :elimine las líneas nuevas finales de cada línea de entrada y agregue una nueva línea a cada print llamar;
  • -a :modo awk, divide cada línea de entrada en la matriz @F;
  • -F :el separador de campo para -a .

Una diferencia importante con awk es eso perl ‘s -a switch divide los archivos en una matriz. En Perl, las matrices comienzan en 0, no en 1. Esto significa que el segundo campo es en realidad $F[1] y no $F[2] . Con todo esto en mente, el perl los equivalentes de los anteriores son:

  • Imprime líneas cuyo tercer campo es foo .

    perl -ane 'print if $F[2] eq "foo"' file
    

    Cambiando el delimitador a :

    perl -F":" -ane 'print if $F[2] eq "foo"' file
    

    A diferencia de awk , perl no puede usar expresiones regulares como delimitadores de campo. Deben ser un carácter o una cadena específicos.

  • ¿Cómo puedo imprimir solo líneas cuyo tercer campo no sea foo? ?

    perl -ane 'print unless $F[2] eq "foo"' file
    
  • ¿Cómo puedo imprimir solo líneas cuyo tercer campo coincida con foo? ?

    perl -ane 'print if $F[2]=~/foo/' file
    
  • ¿Cómo puedo imprimir solo líneas cuyo tercer campo no coincide con foo? ?

    perl -lane 'print unless $F[2]=~/foo/' file
    
  • ¿Cómo puedo cambiar el tercer campo a foo? ?

    Este es un poco más engorroso en Perl. El enfoque habitual es cambiar el valor en @F matriz y luego imprima la matriz. Con archivos simples separados por espacios, esto es fácil:

    perl -lane '$F[2]="foo"; print "@F"' file
    

    Con un delimitador diferente, deberá join la matriz De lo contrario, se imprimirá separado por espacios:

    perl -F: -lane '$F[2]="foo"; print join ":",@F' file
    

2. Usa expresiones regulares

La idea aquí es usar una expresión regular ("regex" para abreviar) que define la posición de la cadena de destino en la línea. Por ejemplo, en un archivo cuyos campos están separados por : , podemos encontrar el segundo campo haciendo coincidir todo hasta el primer : (el primer campo) y luego buscando el segundo:

^[^:]*:[^:]*:

Esta expresión regular significa:

  • ^ :el comienzo de la línea;
  • [^] :una clase de carácter negada. [^:] significa “cualquier cosa menos : “;
  • * :0 o más del patrón anterior;
  • : :un literal :;

En conjunto, esto significa que el primer [^:]* es el primer campo y el segundo es el segundo campo. Obviamente, esto no es muy práctico si está buscando el campo 14, pero puede ser útil para cosas más simples. Entonces, ¿cómo implementamos esto para manipular nuestros datos? Hay varias herramientas que pueden hacer esto; en estos ejemplos usaré sed pero podrías hacer cosas muy similares con awk , perl o python .

  • ¿Cómo puedo imprimir solo líneas cuyo segundo campo es foo? ?

    sed -n '/^[^:]*:foo:/p' file
    

    El -n suprime la salida normal y el /regex/p significa "imprimir cualquier línea que coincida con la expresión regular".

  • ¿Cómo puedo imprimir solo líneas cuyo segundo campo no sea foo? ?

    sed '/^[^:]*:foo:/d' file
    

    El inverso lógico de lo anterior. Aquí, el /regex/d significa "eliminar cualquier línea que coincida con la expresión regular".

  • ¿Cómo puedo imprimir solo líneas cuyo segundo campo coincida con foo? ?

    sed -n '/^[^:]*:[^:]*foo/p' file
    
  • ¿Cómo puedo imprimir solo líneas cuyo segundo campo no coincide con foo? ?

    sed '/^[^:]*:[^:]*foo/d' file
    
  • ¿Cómo puedo cambiar el segundo campo a foo? ?

    sed 's/([^:]*:)[^:]*/1foo/' file 
    

    O, desde sed la sustitución puede abordar directamente la ocurrencia de un patrón mediante su repetición con un indicador numérico simple:

    sed 's/[^:]*/foo/2' file
    

Linux
  1. ¿Cómo eliminar líneas duplicadas dentro de un archivo de texto?

  2. ¿Cómo contar el número de valores únicos de un campo en un archivo de texto delimitado por tabuladores?

  3. ¿Cómo extraer texto de un archivo grande, comenzando en la primera aparición de una cadena?

  4. ¿Cómo convertir texto específico de una lista en mayúsculas?

  5. ¿Cómo mostrar ciertas líneas de un archivo de texto en Linux?

Bash scripting:cómo leer datos de archivos de texto

Cómo imprimir líneas duplicadas en un archivo de texto en Linux

Cómo unir varias líneas en una en un archivo en Linux

Cómo extraer direcciones de correo electrónico de un archivo de texto en Linux

Cómo importar datos en Apache Solr

Cómo hacer eco en un archivo