¿Cómo usar un comando de shell para mostrar solo la primera columna y la última columna en un archivo de texto?

30

Necesito ayuda para descubrir cómo usar el comando sed para mostrar solo la primera columna y la última columna en un archivo de texto. Esto es lo que tengo hasta ahora para la columna 1:

cat logfile | sed 's/\|/ /'|awk '{print $1}'

Mi débil intento de mostrar también la última columna fue:

cat logfile | sed 's/\|/ /'|awk '{print $1}{print $8}'

Sin embargo, esto toma la primera columna y la última columna y las combina en una sola lista. ¿Hay alguna manera de imprimir la primera columna y las últimas columnas claramente con los comandos sed y awk?

Entrada de muestra:

foo|dog|cat|mouse|lion|ox|tiger|bar
usuario70573
fuente
55
Proporcione algunos datos de muestra.
jasonwryan

Respuestas:

51

Casi ahi. Simplemente ponga ambas referencias de columna una al lado de la otra.

cat logfile | sed 's/|/ /' | awk '{print $1, $8}'

También tenga en cuenta que no necesita cataquí.

sed 's/|/ /' logfile | awk '{print $1, $8}'

También tenga en cuenta que puede decir awkque los separadores de columna son |, en lugar de espacios en blanco, por lo que tampoco necesita sed.

awk -F '|' '{print $1, $8}' logfile

Según las sugerencias de Caleb , si desea una solución que aún genere el último campo, incluso si no hay exactamente ocho, puede usarla $NF.

awk -F '|' '{print $1, $NF}' logfile

Además, si desea que la salida retenga los |separadores, en lugar de usar un espacio, puede especificar los separadores de campo de salida. Desafortunadamente, es un poco más torpe que solo usar la -Fbandera, pero aquí hay tres enfoques.

  • Puede asignar los separadores de campo de entrada y salida en awksí mismo, en el bloque COMENZAR.

    awk 'BEGIN {FS = OFS = "|"} {print $1, $8}' logfile
  • Puede asignar estas variables al llamar awkdesde la línea de comando, a través de la -vbandera.

    awk -v 'FS=|' -v 'OFS=|' '{print $1, $8}' logfile
  • o simplemente:

    awk -F '|' '{print $1 "|" $8}' logfile
Gavilán
fuente
44
Buen trabajo desglosando cómo se puede simplificar este problema. Puede agregar una nota sobre cómo usarlo |como un separador de salida en lugar del espacio predeterminado para la concatenación de cadenas. También podría explicar el uso en $NFlugar de la codificación rígida $8para obtener la última columna.
Caleb
12

Simplemente reemplace del primero al último |con un |(o espacio si lo prefiere):

sed 's/|.*|/|/'

Tenga en cuenta que aunque no hay sedimplementación donde |sea ​​especial (siempre y cuando las expresiones regulares extendidas no estén habilitadas a través de -Eo -ren algunas implementaciones), \|sí es especial en algunos como GNU sed. Por lo que debe no escapar |si tiene la intención para que coincida con el |personaje.

Si se reemplaza con espacio y si la entrada ya puede contener líneas con solo una |, entonces, tendrá que tratar eso especialmente, ya |.*|que no coincidirá con ellas. Eso podria ser:

sed 's/|\(.*|\)\{0,1\}/ /'

(es decir, hacer la .*|parte opcional) O:

sed 's/|.*|/ /;s/|/ /'

o:

sed 's/\([^|]*\).*|/\1 /'

Si desea los campos primero y octavo independientemente del número de campos en la entrada, entonces es solo:

cut -d'|' -f1,8


(todos estos funcionarían con cualquier utilidad compatible con POSIX suponiendo que la entrada forme texto válido (en particular, los sedque generalmente no funcionarán si la entrada tiene bytes o secuencias de bytes que no forman caracteres válidos en el entorno local actual, como por ejemplo printf 'unix|St\351phane|Chazelas\n' | sed 's/|.*|/|/'en un entorno local UTF-8)).

Stéphane Chazelas
fuente
11

Estás utilizando de awktodos modos:

awk '{ print $1, $NF }' file
jasonwryan
fuente
2
¿No necesitaría especificar el separador de campo de entrada (ya que en este caso parece ser |más bien ese espacio) con -F\|o similar? Además, ¿qué pasaría si quisiera usar el mismo delimitador para la salida?
Caleb
@Caleb Probablemente: estaba esperando que el OP confirmara exactamente cómo se veía la entrada, en lugar de tratar de adivinar en base a los ejemplos que no funcionan ...
jasonwryan
1
Tenga en cuenta que eso supone que la entrada contiene al menos 2 campos.
Stéphane Chazelas
@ StéphaneChazelas OP declaró claramente en el código que tiene ocho campos, siempre.
michaelb958 - Reinstale a Monica el
3
@ michaelb958 Creo que "claramente" está exagerando el caso, solo un poco :)
jasonwryan
4

Si te encuentras awk y sed-less, puedes lograr lo mismo con coreutils:

paste <(           cut -d'|' -f1  file) \ 
      <(rev file | cut -d'|' -f1 | rev)
Thor
fuente
cutes más limpio y más compacto que awk / sed cuando solo está interesado en la primera columna, o si los delimitadores son fijos (es decir, no hay un número variable de espacios).
Sridhar Sarnobat
2

Parece que intenta obtener el primer y el último campo de texto delimitados por |.

Supuse que su archivo de registro contiene el texto que se muestra a continuación,

foo|dog|cat|mouse|lion|ox|tiger|bar
bar|dog|cat|mouse|lion|ox|tiger|foo

Y quieres la salida como,

foo bar
bar foo

En caso afirmativo, aquí viene el comando para su

A través de GNU sed,

sed -r 's~^([^|]*).*\|(.*)$~\1 \2~' file

Ejemplo:

$ echo 'foo|dog|cat|mouse|lion|ox|tiger|bar' | sed -r 's~^([^|]*).*\|(.*)$~\1 \2~'
foo bar
Avinash Raj
fuente
Las columnas no están delimitadas por una tubería | pero están en columnas, estoy interesado en usar sed pero no en usar el comando awk como lo hizo en su comando: sed -r 's ~ ^ ([^ |] *). * \ | (. *) $ ~ \ 1 \ 2 ~ 'archivo
usuario70573
"Las columnas no están delimitadas por una tubería | pero están en columnas", ¿quiere decir que las columnas están separadas por espacios?
Avinash Raj
Una entrada de muestra y una salida serían mejores.
Avinash Raj
1

Probablemente deberías hacerlo con sed, de todos modos, pero solo porque nadie ha escrito este todavía:

while IFS=\| read col1 cols
do  printf %10s%-s\\n "$col1 |" " ${cols##*|}"
done <<\INPUT
foo|dog|cat|mouse|lion|ox|tiger|bar
INPUT

SALIDA

     foo | bar
mikeserv
fuente