Comando de Linux (como cat) para leer una cantidad específica de caracteres

120

¿Existe un comando como caten Linux que pueda devolver una cantidad específica de caracteres de un archivo?

por ejemplo, tengo un archivo de texto como:

Hello world
this is the second line
this is the third line

Y quiero algo que devuelva los primeros 5 caracteres, que sería "hola".

Gracias

pbreault
fuente
Tenga en cuenta que ninguna de las respuestas dadas consume solo N bytes de una secuencia. Por ejemplo: mkfifo /tmp/test.fifo; echo "hello world">/tmp/test.fifo & head -c 5 /tmp/test.fifotambién consume lo " world\n"que se pierde para siempre.
Yeti

Respuestas:

192

head también funciona:

head -c 100 file  # returns the first 100 bytes in the file

.. extraerá los primeros 100 bytes y los devolverá.

Lo bueno de usar headpara esto es que la sintaxis para tailcoincidencias:

tail -c 100 file  # returns the last 100 bytes in the file

Puede combinarlos para obtener rangos de bytes. Por ejemplo, para obtener los segundos 100 bytes de un archivo, lea los primeros 200 con heady use tail para obtener los últimos 100:

head -c 200 file | tail -c 100
Dan
fuente
@Miffy: lea los primeros 20 bytes con head, luego use tailpara obtener los últimos 10, por ejemplo:head -c 20 file | tail -c 10
Dan
47

Puede utilizar dd para extraer trozos arbitrarios de bytes.

Por ejemplo,

dd skip=1234 count=5 bs=1

copiaría los bytes 1235 a 1239 de su entrada a su salida y descartaría el resto.

Para obtener los primeros cinco bytes de la entrada estándar, haga lo siguiente:

dd count=5 bs=1

Tenga en cuenta que, si desea especificar el nombre del archivo de entrada, dd tiene un análisis de argumentos anticuado, por lo que debería hacer:

dd count=5 bs=1 if=filename

Tenga en cuenta también que dd anuncia detalladamente lo que hizo, por lo que para descartarlo, haga lo siguiente:

dd count=5 bs=1 2>&-

o

dd count=5 bs=1 2>/dev/null
fcw
fuente
2
Recomendaría esta solución en general, ya que dd bs=1obliga a dd a leer y escribir un solo carácter a la vez, lo que es mucho más lento que headcuando el recuento es grande. Sin embargo, no se nota para count = 5.
Ephemient
2
¿Qué pasa con "dd count = 1 bs = 5"? Eso habría leído cinco bytes de una vez. Aún así, la cabeza es probablemente una solución más clara.
Ben Combee
1
Gracias por esto. De hecho, estaba buscando una manera de "cortar" un archivo binario y dd, al parecer, funcionará ... ¡Salud!
sdaau
esto fue un salvavidas en busybox sin head -cimplementar el dd bs=5 count=1enfoque funcionó
Jay Paroline
11

cabeza :

Nombre

head: genera la primera parte de los archivos

Sinopsis

cabeza [ OPCIÓN ] ... [ ARCHIVO ] ...

Descripción

Imprima las primeras 10 líneas de cada ARCHIVO en salida estándar. Con más de un ARCHIVO, preceda a cada uno con un encabezado que indique el nombre del archivo. Sin FILE, o cuando FILE es -, lea la entrada estándar.

Los argumentos obligatorios para las opciones largas también son obligatorios para las opciones cortas.
-c , --bytes = [-] N imprime los primeros N bytes de cada archivo; con el '-' inicial, imprime todos menos los últimos N bytes de cada archivo

gimel
fuente
3

la cabeza o la cola también pueden hacerlo:

cabeza -c X

Imprime los primeros X bytes (no necesariamente caracteres si es un archivo UTF-16) del archivo. tail hará lo mismo, excepto por los últimos X bytes.

Esto (y el corte) son portátiles.

Zathrus
fuente
3
head -Line_number file_name | tail -1 |cut -c Num_of_chars

este script proporciona el número exacto de caracteres de la línea y ubicación específicas, por ejemplo:

head -5 tst.txt | tail -1 |cut -c 5-8

da los caracteres en la línea 5 y los caracteres 5 a 8 de la línea 5,

Nota : tail -1se utiliza para seleccionar la última línea mostrada por el encabezado.

Vignesh
fuente
2

también puede grep de la línea y luego cortarla como, por ejemplo:

grep 'texto' nombre de archivo | cortar -c 1-5

nkr1pt
fuente
Esto no funciona si el archivo de entrada es una secuencia interminable sin \ n
Ajay Brahmakshatriya
2

Sé que la respuesta es en respuesta a una pregunta hecha hace 6 años ...

Pero estuve buscando algo similar durante unas horas y luego descubrí que: cut -c hace exactamente eso, con una ventaja adicional de que también podría especificar un desplazamiento.

cut -c 1-5 devolverá Hello y cut -c 7-11 devolverá world . No necesita ningún otro comando

bobbyus
fuente
2
¡Tienes razón !. Solo quería resaltar la posibilidad de un comando único más genérico que pueda devolver texto desde el medio de un archivo a diferencia de head -c leerá solo los caracteres iniciales, tail -c los últimos caracteres. Y sin usar grep :).
bobbyus
2

Aunque esto fue respondido / aceptado hace años, la respuesta actualmente aceptada solo es correcta para codificaciones de un byte por carácter como iso-8859-1, o para los subconjuntos de un byte de conjuntos de caracteres de bytes variables (como caracteres latinos dentro de UTF-8). Incluso el uso de empalmes de varios bytes en su lugar solo funcionaría para codificaciones fijas de varios bytes como UTF-16. Dado que ahora UTF-8 está en camino de convertirse en un estándar universal, y al mirar esta lista de idiomas por número de hablantes nativos y esta lista de los 30 idiomas principales por uso nativo / secundario , es importante señalar un Técnica simple y amigable con los caracteres de bytes variables (no basada en bytes), usando cut -cy tr/ sedcon clases de caracteres.

Compare lo siguiente que falla doblemente debido a dos errores / presunciones comunes centrados en el latín con respecto al problema de bytes frente a caracteres (uno es headvs. cut, el otro es [a-z][A-Z]vs. [:upper:][:lower:]):

$ printf 'Πού μπορώ να μάθω σανσκριτικά;\n' | \
$     head -c 1 | \
$     sed -e 's/[A-Z]/[a-z]/g'
[[unreadable binary mess, or nothing if the terminal filtered it]]

a esto (nota: esto funcionó bien en FreeBSD, pero ambos cuty tren GNU / Linux todavía destrozaron el griego en UTF-8 para mí):

$ printf 'Πού μπορώ να μάθω σανσκριτικά;\n' | \
$     cut -c 1 | \
$     tr '[:upper:]' '[:lower:]'
π

Otra respuesta más reciente ya había propuesto "cortar", pero solo debido al problema secundario de que se puede usar para especificar compensaciones arbitrarias, no debido al problema de caracteres frente a bytes directamente relevante.

Si cutno maneja -ccorrectamente las codificaciones de bytes variables, para "los primeros Xcaracteres" (reemplace Xcon su número) puede intentar:

  • sed -E -e '1 s/^(.{X}).*$/\1/' -e q - que se limita a la primera línea, aunque
  • head -n 1 | grep -E -o '^.{X}' - que se limita a la primera línea y encadena dos comandos aunque
  • dd - que ya se sugirió en otras respuestas, pero es realmente engorroso
  • Un sedscript complicado con búfer de ventana deslizante para manejar caracteres repartidos en varias líneas, pero que probablemente sea más engorroso / frágil que simplemente usar algo comodd

Si trno maneja correctamente las clases de caracteres con codificaciones de bytes variables, puede intentar:

  • sed -E -e 's/[[:upper:]]/\L&/g (Específico de GNU)
rowanthorpe
fuente
lo siento, pero no funciona aquí ... printf 'Πού ' | cut -c 1solo devuelve galimatías ... se comporta como 'cabeza'
LEo
según la documentación en línea, aún no está disponible: "Seleccione para imprimir solo los caracteres en las posiciones enumeradas en la lista de caracteres. Lo mismo que -b por ahora, pero la internacionalización cambiará eso". [ gnu.org/software/coreutils/manual/html_node/…
LEo
@LEo Según el enlace en su segundo comentario, parece que está utilizando un sistema operativo basado en GNU, presumiblemente GNU / Linux, por lo que en ese caso se espera, lo menciono al final de mi respuesta. Funcionó entonces (y funciona ahora) para mí en FreeBSD (y probablemente en algunos otros sistemas operativos) pero no funcionó (y todavía no funciona) en GNU / Linux, para ese caso mencioné los métodos alternativos al final. Personalmente, no puedo esperar hasta que alguien encuentre y se ofrezca como voluntario el tiempo libre para realizar la internacionalización necesaria para que el conjunto de herramientas GNU funcione tan bien como los demás en ese sentido.
rowanthorpe
0

Aquí hay un script simple que concluye usando el ddenfoque mencionado aquí:

extract_chars.sh

#!/usr/bin/env bash

function show_help()
{
  IT="
extracts characters X to Y from stdin or FILE
usage: X Y {FILE}

e.g. 

2 10 /tmp/it     => extract chars 2-10 from /tmp/it
EOF
  "
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi
if [ -z "$1" ]
then
  show_help
fi

FROM=$1
TO=$2
COUNT=`expr $TO - $FROM + 1`

if [ -z "$3" ]
then
  dd skip=$FROM count=$COUNT bs=1 2>/dev/null
else
  dd skip=$FROM count=$COUNT bs=1 if=$3 2>/dev/null 
fi
Brad Parks
fuente