Edición de archivos con sed#

sed es un editor de flujos que puede ser usado para extraer, adicionar o reemplazar textos en un archivo. En Ciencia de los datos, resulta particularmente interesante para realizar impresión, sustitución y borrado de textos en archivos planos, que es una tarea común en procesos ETL y data pipe lines. Cada comando es representado por una letra y el carácter / es usado como un delimitador.

En este tutorial usted aprenderá a:

  • Imprimir las líneas de un archivo que esten en una posición específica.

  • Imprimir las líneas de un archivo que cumplan con un criterio específico.

  • Borrar líneas.

  • Substituir texto.

  • Agregar contenido a un archivo.

Manual y ayuda en línea#

man sed

Impresión de líneas específicias#

[1]:
%%bash
#
# Se imprimen los primeros 30 números a un archivo
#
seq 30 > out.1
[2]:
%%bash
#
# Imprime la línea 3
#
sed -n '3p' out.1
3
[3]:
%%bash
#
# imprime las líneas 3 a 6
#
sed -n '3,6p' out.1
3
4
5
6
[4]:
%%bash
#
# imprime las líneas 3 a 6 y 11 a 13
#
sed -n '3,6p; 11,13p' out.1
3
4
5
6
11
12
13
[5]:
%%bash
#
# de la linea 25 al final
#
sed -n '25,$ p' out.1
25
26
27
28
29
30

Filtrado#

[6]:
%%bash
#
# Imprime las lineas que contengan un 1
#
sed -n '/1/p'  out.1
1
10
11
12
13
14
15
16
17
18
19
21
  • La opción -n indica que no debe imprimirse en pantalla cada línea leída del archivo out.1.

  • La cadena /1/ indica la expresión regular (en este caso que la línea contenga un 1 y los / son delimitadores). La p al final indica que se imprima la línea.

[7]:
%%bash
#
# Imprime las líneas que tengaun un 1 al final
#
sed -n '/1$/p'  out.1
1
11
21
[8]:
%%sh
## Imprime las líneas que tengan un 1 al inicio
sed -n '/^1/p' out.1
1
10
11
12
13
14
15
16
17
18
19

Borrado#

[9]:
%%bash
#
# Borra las líneas 3 a la 26
#
sed "3,26 d" out.1
1
2
27
28
29
30

Encadenado de expresiones#

La opción -e con sed indica expresión (comando) y permite realizar comandos simultaneos; por ejemplo, imprimir todo excepto la posición 1 y 2.

[10]:
%%bash
sed -e 1d -e 2d out.1
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

Substitución#

En el archivo generado mediante las siguientes instrucciones:

[11]:
%%bash
cat > out.1 <<EOF
FieldA, FieldD, FieldE, FieldG
   2, X, 2X, 2XG
   2, Y, 2Y, 2YG
   3, Y, 3Y, 3YG
   3, X, 3X, 3XG
   4, Z, 4Z, 3XG
EOF

se desean cambiar las X por x.

Para ello, se emplea la opción s para indicar que se realizarán sustituciones de texto:

[12]:
%%bash
#
# Cambia la primera ocurrencia de X por x en cada linea
#
sed 's/X/x/' out.1
FieldA, FieldD, FieldE, FieldG
   2, x, 2X, 2XG
   2, Y, 2Y, 2YG
   3, Y, 3Y, 3YG
   3, x, 3X, 3XG
   4, Z, 4Z, 3xG

Note que solo se sustituyeron la primera ocurrencia de cada línea. Si se requieren cambiar todas las ocurrencias en cada línea se usa g para indicar sustitución global:

[13]:
%%bash
#
# Cambia todas las ocurrencias de X por x
#
sed 's/X/x/g' out.1
FieldA, FieldD, FieldE, FieldG
   2, x, 2x, 2xG
   2, Y, 2Y, 2YG
   3, Y, 3Y, 3YG
   3, x, 3x, 3xG
   4, Z, 4Z, 3xG

La opción y es útil para transformar el registro en el orden en el que se indique en la expresión regular; por ejemplo, se requiere cambiar sistemáticamente las X por x y las Y por y.

[14]:
%%bash
sed 'y/XY/xy/' out.1
FieldA, FieldD, FieldE, FieldG
   2, x, 2x, 2xG
   2, y, 2y, 2yG
   3, y, 3y, 3yG
   3, x, 3x, 3xG
   4, Z, 4Z, 3xG

Se pueden realizar sustituciones de acuerdo con las veces que se encuentre el patrón en la linea; por ejemplo, se requiere sustituir las X por el simbolo # en el archivo pero solo para la segunda columna en donde se encuentren letras X.

[15]:
%%bash
sed 's/X/#/2' out.1
FieldA, FieldD, FieldE, FieldG
   2, X, 2#, 2XG
   2, Y, 2Y, 2YG
   3, Y, 3Y, 3YG
   3, X, 3#, 3XG
   4, Z, 4Z, 3XG

La opción 2g indica que se reemplace cuando encuentre el patrón por segunda vez y en adelante.

[16]:
%%bash
sed 's/X/xx/2g' out.1
FieldA, FieldD, FieldE, FieldG
   2, X, 2xx, 2xxG
   2, Y, 2Y, 2YG
   3, Y, 3Y, 3YG
   3, X, 3xx, 3xxG
   4, Z, 4Z, 3XG

El simbolo & se usa en una expresión regular para indicar la cadena de texto reconocida. Es decir, el símbolo & es reemplazado por la cadena de texto reconocida; por ejemplo, se requiere tener las letras X entre llaves para la linea 5.

[17]:
%%bash
sed '5 s/X/{&}/g' out.1
FieldA, FieldD, FieldE, FieldG
   2, X, 2X, 2XG
   2, Y, 2Y, 2YG
   3, Y, 3Y, 3YG
   3, {X}, 3{X}, 3{X}G
   4, Z, 4Z, 3XG

Sea el siguiente archivo:

[18]:
%%bash
cat > out.1 <<EOF
1980-JAN-1+1:0:1.134
1980-JAN-5+1:0:1.12
1982-JAN-13+10:12:42.33
EOF

Se desea formatear la fecha y la hora, es decir, la primera línea:

1980-JAN-1+1:0:1.134

debe cambiarse por:

1980-JAN-01 01:00:01

El primer paso consiste en agregar el cero a los números de día con un solo dígito.

[19]:
%%bash
sed 's/-\([0-9]\)+/-0\1+/' out.1 > out.2
cat out.2
1980-JAN-01+1:0:1.134
1980-JAN-05+1:0:1.12
1982-JAN-13+10:12:42.33

La explicación del comando anterior es la siguiente. El patrón de entrada está conformado por los siguientes elementos:

  • El caracter -.

  • Un dígito entre 0 y 9 (patrón [0-9]). Las secuencias \( y \) especifican que el dígito reconocido debe recordarse. Pueden existir varias cadenas a recordar; la primera cadena es \1, la segunda cadena es \2 y así sucesivamente.

  • El caracter +.

El patrón de salida indica que:

  • Se imprime el caracter -.

  • Luego el caracter 0.

  • A continuación el dígito reconocido \1.

Se reemplaza el + por un espacio en blanco.

[20]:
%%bash
sed 's/+/ /' out.2 > out.3
cat out.3
1980-JAN-01 1:0:1.134
1980-JAN-05 1:0:1.12
1982-JAN-13 10:12:42.33

Se agrega el 0 a las horas. La expresión regular indica que es un espacio en blanco seguido de un dígito, seguido de :.

[21]:
%%bash
sed 's/ \([0-9]\):/ 0\1:/' out.3 > out.4
cat out.4
1980-JAN-01 01:0:1.134
1980-JAN-05 01:0:1.12
1982-JAN-13 10:12:42.33

Se agrega el 0 a los minutos.

[22]:
%%bash
sed 's/:\([0-9]\):/:0\1:/' out.4 > out.5
cat out.5
1980-JAN-01 01:00:1.134
1980-JAN-05 01:00:1.12
1982-JAN-13 10:12:42.33

Se agrega el 0 a los segundos.

[23]:
%%bash
sed 's/:\([0-9]\)\./:0\1./' out.5 > out.6
cat out.6
1980-JAN-01 01:00:01.134
1980-JAN-05 01:00:01.12
1982-JAN-13 10:12:42.33

Se elimina la parte decimal de los segundos

[24]:
%%bash
sed 's/\.[0-9][0-9]*//' out.6 > out.7
cat out.7
1980-JAN-01 01:00:01
1980-JAN-05 01:00:01
1982-JAN-13 10:12:42

La notación \.[0-9][0-9]* indica que el patrón es un punto (\.) seguido de un dígito ([0-9]), seguido de cero, uno o más dígitos ([0-9]*).

Adición de contenido#

Sea el siguiente archivo:

[25]:
%%bash
cat > out.1 <<EOF
AAAA 1234 SI
BBBB 5678 SI
CCCC 9012 NO
EOF
  • La función sed junto con la opción i, permiten agregar lineas antes de un texto que es reconocido mediante expresión regular. De la misma forma, la opción a permite agregar lineas después del texto reconocido.

  • La función sed junto con la opción c, permiten cambiar la información de un registro que cumpla un patrón específico.

Se requiere incluir antes del registro BBBB, la siguiente información:

EEEE 0000 SI

[26]:
%%bash
sed '/AAAA/ i\EEEE 0000 SI/' out.1
EEEE 0000 SI/
AAAA 1234 SI
BBBB 5678 SI
CCCC 9012 NO

Se requiere cambiar el registro BBBB por la siguiente información:

XXXX 1111 SI

[27]:
%%bash
sed '/BBBB/ c XXXX 1111 SI' out.1
AAAA 1234 SI
XXXX 1111 SI
CCCC 9012 NO

Creación de nuevos campos#

Sea el siguiente archivo:

[28]:
%%bash
cat > out.1 <<EOF
Date, Year, CustomerID, Value
2013-01-12, 2013, 1, 100
2014-05-12, 2014, 1, 100
2013-02-25, 2013, 2, 200
2013-04-04, 2013, 1, 100
2013-06-21, 2013, 2, 200
2014-05-12, 2014, 12, 100
2014-05-12, 2014, 2, 200
2013-02-28, 2013, 11, 100
2013-08-02, 2013, 1, 100
EOF

Se desea agregar un nuevo campo llamado Year-CoustomerID que contiene una clave compuesta conformada por la concatenación de estos dos campos; por ejemplo, el valor para el primer registro sería 2013-1. El siguiente comando hace el cambio del reglón dos en adelante:

[29]:
%%bash
sed 's/ \([0-9][0-9][0-9][0-9]\), \([0-9]*\)/ \1, \2, \1-\2/' out.1 > out.2
cat out.2
Date, Year, CustomerID, Value
2013-01-12, 2013, 1, 2013-1, 100
2014-05-12, 2014, 1, 2014-1, 100
2013-02-25, 2013, 2, 2013-2, 200
2013-04-04, 2013, 1, 2013-1, 100
2013-06-21, 2013, 2, 2013-2, 200
2014-05-12, 2014, 12, 2014-12, 100
2014-05-12, 2014, 2, 2014-2, 200
2013-02-28, 2013, 11, 2013-11, 100
2013-08-02, 2013, 1, 2013-1, 100

Para realizar el cambio en la primera línea (el encabezado) se usaría el siguiente comando:

[30]:
%%bash
sed 's/\([a-zA-Z]*\), \([a-zA-Z]*\), \([a-zA-Z]*\), \([a-zA-Z]*\)/\1, \2, \3, \2-\3, \4/' out.2
Date, Year, CustomerID, Year-CustomerID, Value
2013-01-12, 2013, 1, 2013-1, 100
2014-05-12, 2014, 1, 2014-1, 100
2013-02-25, 2013, 2, 2013-2, 200
2013-04-04, 2013, 1, 2013-1, 100
2013-06-21, 2013, 2, 2013-2, 200
2014-05-12, 2014, 12, 2014-12, 100
2014-05-12, 2014, 2, 2014-2, 200
2013-02-28, 2013, 11, 2013-11, 100
2013-08-02, 2013, 1, 2013-1, 100

Resumen#

## imprime las líneas 3 a 6
sed -n '3,6p' out.1

## de la linea 25 al final
sed -n '25,$ p' out.1

## Imprime las lineas que contengan un 1
sed -n '/1/p'  out.1

## Imprime las líneas que tengan un 1 al inicio
sed -n '/^1/p' out.1

## Imprime las líneas que tengaun un 1 al final
sed -n '/1$/p'  out.1

# Imprime todas las lineas menos de la 3 a la 26
sed "3,26 d" out.1

## Reemplaza la primera ocurrencia de X por x en cada linea
sed 's/X/x/' out.1

## Reemplaza todas las ocurrencias de X por x
sed 's/X/x/g' out.1

## Reemplaza la segunda ocurrencias de X por x
sed 's/X/#/2' out.1

## Reemplaza de la segunda ocurrencias de X por x en adelante
sed 's/X/#/2' out.1

## Reemplaza X por {X} solo en la 5a linea
sed '5 s/X/{&}/g' out.1

Borrado de los archivos temporales creados

[31]:
!rm out.*