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

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 la última columna también fue:

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

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

Ejemplo de entrada:

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

Comentarios

  • Proporcione una entrada de muestra.

Respuesta

Casi llego. Simplemente coloque las referencias de ambas columnas una al lado de la otra.

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

También tenga en cuenta que no necesita cat aquí .

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

También tenga en cuenta que puede decirle a awk que 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 muestre el último campo , incluso si no hay exactamente ocho, puede usar $NF.

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

Además, si desea salida para retener los separadores |, en lugar de usar un espacio, puede especificar los separadores de campo de salida. Desafortunadamente, es un poco más torpe que simplemente usar el indicador -F, pero aquí hay tres enfoques.

  • Puede asignar la entrada y separadores de campo de salida en awk mismo, en el bloque BEGIN.

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

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

    awk -F "|" "{print $1 "|" $8}" logfile 

Comentarios

  • Buen trabajo desglosando cómo se puede simplificar este problema. Podría agregar una nota sobre cómo usar | como separador de salida en lugar de el espacio predeterminado para la concatenación de cadenas. También puede explicar el uso de $NF en lugar de codificar $8 para obtener la última columna.
  • después de eso, ¿cómo actualizar el archivo?
  • @pankajprasad Escribir en un nuevo archivo con h > luego sobrescriba el anterior o use sponge. Sin embargo, esta es realmente una pregunta nueva.
  • @Sparhawk funciona, pero se borra el contenido de escariado. ¿cómo lidiar con eso?
  • @pankajprasad Necesitas hacer una nueva pregunta. Haga clic en el gran botón azul en la parte superior que dice » Preguntar «.

Responder

De todos modos, estás usando awk:

awk "{ print $1, $NF }" file 

Comentarios

  • No ‘ t necesita especificar el separador de campo de entrada (ya que en este caso parece ser | en lugar de ese espacio) con -F\| o similar? Además, ¿qué pasaría si quisiera usar el mismo delimitador para la salida?
  • @Caleb Probablemente: estaba esperando que el OP confirmara cómo exactamente era la entrada, en lugar de intentar adivinar basado en los ejemplos que no funcionan …
  • Tenga en cuenta que se supone que la entrada contiene al menos 2 campos.
  • @St é phaneChazelas OP indicó claramente en el código que tiene ocho campos, siempre.
  • @ michaelb958 Creo » claramente » está exagerando el caso, solo un poco 🙂

Respuesta

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

sed "s/|.*|/|/" 

Tenga en cuenta que, aunque no hay una implementación sed donde | sea especial (siempre que extendido las expresiones no están habilitadas a través de -E o en algunas implementaciones), \| en sí es especial en algunas como GNU sed. Por lo tanto, no debe escapar de | si desea que coincida con el carácter |.

Si reemplaza con espacio y si la entrada ya puede contener líneas con solo un |, entonces, «tendrá que tratarlo especialmente como |.*| no coincidirá con esos.Podría ser:

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

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

sed "s/|.*|/ /;s/|/ /" 

o:

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

Si desea el primer y octavo campo independientemente del número de campos en la entrada, entonces es simplemente:

cut -d"|" -f1,8 

(todos estos funcionarían con cualquier utilidad compatible con POSIX asumiendo la entrada forma texto válido (en particular, los sed generalmente no funcionarán si la entrada tiene bytes o secuencias de bytes que no forman caracteres válidos en la configuración regional actual como por ejemplo printf "unix|St\351phane|Chazelas\n" | sed "s/|.*|/|/" en una configuración regional UTF-8)).

Respuesta

Si te encuentras sin awk y sin sed, puedes lograr el lo mismo con coreutils:

paste <( cut -d"|" -f1 file) \ <(rev file | cut -d"|" -f1 | rev) 

Comentarios

  • cut es más limpio y compacto que awk / sed cuando solo está interesado en la primera columna, o si los delimitadores son fijos (es decir, no un número variable de espacios).
  • ¡Bastante elegante!

Responder

Parece que estás intentando obtener el primer y último campo de texto que está delimitado por |.

Asumí 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 desea la salida como,

foo bar bar foo 

Si es así, aquí viene el comando para su «s

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 

Comentarios

  • Las columnas no están delimitadas por una tubería | pero están en columnas, estoy interesado en usar sed pero no usar el comando awk como lo hizo en su comando: sed -r ‘ s ~ ^ ([^ |] *) . * \ | (. *) $ ~ \ 1 \ 2 ~ ‘ archivo
  • » Las columnas son no delimitado por una tubería | pero están en columnas «, ¿quiere decir que las columnas están separadas por espacios?
  • Una entrada de muestra y una salida sería mejor.

Respuesta

Probablemente deberías hacerlo con sed – lo haría 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 

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *