obtener los primeros X caracteres del comando cat?

42

Tengo un archivo de texto que estoy enviando a una variable en mi script de shell. Sin embargo, solo necesito los primeros 50 caracteres.

He intentado usar cat ${filename} cut -c1-50pero obtengo mucho más que los primeros 50 caracteres. Eso puede deberse a la cutbúsqueda de líneas (no 100% seguro), mientras que este archivo de texto podría ser una cadena larga, realmente depende.

¿Hay alguna utilidad en la que pueda canalizar para obtener los primeros caracteres X de un catcomando?

jkj2000
fuente
10
¿Olvidaste un |? cat ${filename} | cut -c1-50
DisplayName
@DisplayName solucionado, gracias por detectar mi error de reescritura.
jkj2000
1
@ jkj2000, he vuelto a la versión anterior, ya que esa era la pregunta original.
Ramesh

Respuestas:

61
head -c 50 file

Esto devuelve los primeros 50 bytes.

Tenga en cuenta que el comando no siempre se implementa de la misma manera en todos los sistemas operativos. En Linux y macOS se comporta de esta manera. En Solaris (11) necesita usar la versión gnu en / usr / gnu / bin /

Nombre para mostrar
fuente
La cabeza no tiene -copción. Iría por dd (1) en su lugar.
mirabilos
77
Tenga en cuenta que esta respuesta supone que el archivo contiene solo caracteres ASCII, ya que el OP solicitó los primeros X caracteres, no bytes.
Calimo
2
@mirabilos Puede que no sea portátil, pero mi versión ( GNU coreutils 5.97) sí.
Yossarian el
1
-cSin embargo, POSIX no se define como una opción válida, por lo que definitivamente depende de su entorno local. unix.com/man-page/posix/1/head
Julio
1
@Calimo Sí, lo sé, pero intenté crear un archivo de texto con 100 caracteres, luego ejecuté mi comando e imprimió 50 caracteres. Pero tienes razón sobre ASCII, pero desde que OP marcó esto como respondido, no había ninguno en su caso.
DisplayName
27

Su cutcomando funciona si usa una tubería para pasarle datos:

cat ${file} | cut -c1-50 

O, evitando un uso inútil del gato y haciéndolo un poco más seguro:

cut -c1-50 < "$file"

Tenga en cuenta que los comandos anteriores imprimirán los primeros 50 caracteres (o bytes, según su cutimplementación) de cada línea de entrada . Debería hacer lo que espera si, como usted dice, su archivo es una línea enorme.

terdon
fuente
8
dd status=none bs=1 count=50 if=${filename}

Esto devuelve los primeros 50 bytes.

doneal24
fuente
dd no tiene status=nonebandera. Use en su 2>/dev/nulllugar (y cite correctamente): dd if="$filename" bs=1 count=50 2>/dev/null(aun así, considere usar bs=50 count=1para reducir la cantidad de llamadas al sistema involucradas).
mirabilos
1
@mirabilos dd tiene status=nonecuando usa Ubuntu 14.04, coreutils 8.21, pero tiene derecho a usarlo 2>/dev/nullsi usa una versión anterior.
doneal24
1
@mirabilos La mayoría de las distribuciones de Linux usan coreutils de GNU al igual que FreeBSD y otros BSD. Está disponible en Solaris como paquete gnu-coreutils. Sí, esto es "Unix y Linux" y los sistemas Unix y Linux usan GNU coreutils.
doneal24
2
No, los sistemas Unix generalmente no usan utilidades GNU. GNU es un acrónimo de "GNU no es Unix", incluso. Apéguese a las soluciones portátiles o, si debe dar soluciones solo GNU, indíquelo y, si es posible, muestre una solución portátil equivalente.
mirabilos
1
Estrictamente hablando, eso hace uno read()de 50 bytes. Si, filepor ejemplo, es una tubería y hay menos caracteres disponibles en ese momento, se devolverán menos bytes. Para tener el equivalente de head -c50, necesitarías usar el GNU específico iflag=fullblock.
Stéphane Chazelas
4

La mayoría de las respuestas hasta ahora suponen que 1 byte = 1 carácter, lo que puede no ser el caso si está utilizando una configuración regional no ASCII.

Una forma un poco más robusta de hacerlo:

testString=$(head -c 200 < "${filename}") &&
  printf '%s\n' "${testString:0:50}"

Tenga en cuenta que esto supone:

  1. Está utilizando ksh93, bash(o un reciente zsho mksh(aunque el único conjunto de caracteres de varios bytes admitido por mkshUTF-8 y solo después set -o utf8-mode)) y una versión de headese soporte -c(la mayoría lo hace hoy en día, pero no estrictamente estándar).
  2. La configuración regional actual está configurada con la misma codificación que el archivo (escriba locale charmapy file -- "$filename"para verificar eso); si no, configúrelo con ie. LC_ALL=en_US.UTF-8)
  3. Tomé los primeros 200 bytes del archivo head, asumiendo el peor de los casos UTF-8 donde todos los caracteres están codificados en un máximo de 4 bytes. Esto debería cubrir la mayoría de los casos que se me ocurren.
Calimo
fuente
Por supuesto, esto también supone GNU head, u otra implementación de la misma que agrega la -copción nōn-standard . Pero ya estás requiriendo GNU bash. (Nota: mkshel modo UTF-8 podría hacer esto para los archivos codificados UTF-8). Le preguntaría al OP si requieren octetos o caracteres multibyte, solo "caracteres" es un término vago / genérico.
mirabilos
Eso también supone $filenameo $testStringno contiene nueva línea en blanco o comodines o comienza con -.
Stéphane Chazelas
La ${var:offset:length}construcción que está utilizando aquí en realidad proviene ksh93y también es compatible con versiones recientes de zsh( zshtiene la suya propia $testString[1,50]). Necesitas ${testString:0:50} en ksh93y zshsin embargo.
Stéphane Chazelas
Acabo de editar mi respuesta para abordar los comentarios anteriores
Calimo
2
grep -om1 "^.\{50\}" ${filename}

Otra variante (para la primera línea del archivo)

(IFS= read -r line <${filename}; echo ${line:0:50})
Costas
fuente
Esto es abuso de herramientas de alto nivel, y es propenso a no hacer lo que quiere, por ejemplo, si son conscientes de la ubicación.
mirabilos
@mirabilos ¿Qué quieres decir con herramientas de alto nivel : ready echo? O bash expansion?
Costas
grep(regexp), y sí, el uso de shell aquí (pista: la primera línea puede ser grande). (Dicho esto, el bashism tampoco está en POSIX, pero la mayoría de los proyectiles lo implementan)
Mirabilos
0

1. Para archivos ASCII, haga como @DisplayName dice:

head -c 50 file.txt

imprimirá los primeros 50 caracteres de file.txt, por ejemplo.

2. Para datos binarios, use hexdumppara imprimirlos como caracteres hexadecimales:

hexdump -n 50 -v file.bin

imprimirá los primeros 50 bytes de file.bin, por ejemplo.

Tenga en cuenta que sin la -vopción detallada, hexdumpreemplazaría las líneas repetidas con un asterisco ( *) en su lugar. Ver aquí: https://superuser.com/questions/494245/what-does-an-asterisk-mean-in-hexdump-output/494613#494613 .

Gabriel Staples
fuente
-2

Puede usar sed para esto, que abordará el problema con bastante facilidad

sed -e 's/^\(.\{50\}\).*/\1/' yourfile
munkeyoto
fuente
Curioso por saber cómo se votó si esto resuelve la pregunta del OP: "Solo necesito los primeros 50 caracteres" Esto cumple lo que se solicitó sin UUOC (uso inútil del gato)
munkeyoto
1
Esta respuesta proporciona los primeros cincuenta caracteres de cada línea en el archivo, no solo los primeros 50 del archivo. Tampoco imprime nada si todas las líneas tienen menos de 50 caracteres. Su solución funcionaría mejor consed -n -e '1s/^\(.\{50\}\).*/\1/p' ${filename}
doneal24
Entendido podría haber simplemente: head -n 1 | sed -e 's / ^ (. \ {50 \}). * / \ 1 /' ... Y habría resuelto el problema. OP declaró: "solo necesito los primeros 50 caracteres"
munkeyoto
1
No Si la primera línea tiene solo 49 caracteres, no generará nada.
doneal24
Doug, entendí esto la primera vez, sin embargo, el OP no mencionó nada acerca de la impresión si la línea contenía menos de 50 caracteres, por lo que todavía no puedo ver su punto, ni el punto de que se rechace, ya que nuevamente cayó en lo que habría funcionado head: head -n 1 $ {filename} | sed -n -e '1s / ^ (. \ {50 \}). * / \ 1 / p'
munkeyoto