Comando Linux (como cat) para ler uma quantidade especificada de caracteres

120

Existe um comando como catno Linux que pode retornar uma quantidade especificada de caracteres de um arquivo?

por exemplo, eu tenho um arquivo de texto como:

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

E eu quero algo que retorne os primeiros 5 caracteres, que seria "olá".

obrigado

pbreault
fonte
Observe que nenhuma das respostas fornecidas consome apenas N bytes de um fluxo. Por exemplo: mkfifo /tmp/test.fifo; echo "hello world">/tmp/test.fifo & head -c 5 /tmp/test.fifotambém consome o " world\n"que se perde para sempre.
Yeti

Respostas:

192

head funciona também:

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

.. extrairá os primeiros 100 bytes e os retornará.

O que é bom em usar headpara isso é que a sintaxe para tailcorrespondências:

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

Você pode combiná-los para obter intervalos de bytes. Por exemplo, para obter os segundos 100 bytes de um arquivo, leia os primeiros 200 com heade use tail para obter os últimos 100:

head -c 200 file | tail -c 100
Dan
fonte
@Miffy: Leia os primeiros 20 bytes com heade use tailpara obter os últimos 10, por exemplo:head -c 20 file | tail -c 10
Dan
47

Você pode usar o dd para extrair pedaços arbitrários de bytes.

Por exemplo,

dd skip=1234 count=5 bs=1

copiaria os bytes 1235 a 1239 de sua entrada para sua saída e descartaria o resto.

Para obter apenas os primeiros cinco bytes da entrada padrão, faça:

dd count=5 bs=1

Observe que, se você quiser especificar o nome do arquivo de entrada, dd tem análise de argumento antiquada, então você faria:

dd count=5 bs=1 if=filename

Observe também que dd anuncia detalhadamente o que fez, então, para jogá-lo fora, faça:

dd count=5 bs=1 2>&-

ou

dd count=5 bs=1 2>/dev/null
fcw
fonte
2
Eu não recomendo essa solução em geral, pois dd bs=1força o dd a ler e escrever um único caractere por vez, o que é muito mais lento do que headquando a contagem é grande. Não é perceptível para count = 5, no entanto.
efemiente de
2
E quanto a "dd count = 1 bs = 5"? Isso faria com que a cabeça lesse cinco bytes de uma vez. Ainda assim, a cabeça é provavelmente uma solução mais clara.
Ben Combee de
1
Obrigado por isso - na verdade, eu estava procurando uma maneira de 'cortar' um arquivo binário e dd, ao que parece, vou resolver o problema ... Saúde!
sdaau
este foi um salva-vidas no Busybox sem head -cimplementar a dd bs=5 count=1abordagem funcionou
Jay Paroline
11

cabeça :

Nome

head - mostra a primeira parte dos arquivos

Sinopse

cabeça [ OPÇÃO ] ... [ ARQUIVO ] ...

Descrição

Imprima as primeiras 10 linhas de cada ARQUIVO na saída padrão. Com mais de um ARQUIVO, preceda cada um com um cabeçalho com o nome do arquivo. Sem FILE, ou quando FILE for -, leia a entrada padrão.

Argumentos obrigatórios para opções longas também são obrigatórios para opções curtas.
-c , --bytes = [-] N imprime os primeiros N bytes de cada arquivo; com o '-' inicial, imprime todos, exceto os últimos N bytes de cada arquivo

gimel
fonte
3

cabeça ou cauda também podem fazer isso:

head -c X

Imprime os primeiros X bytes (não necessariamente caracteres se for um arquivo UTF-16) do arquivo. tail fará o mesmo, exceto para os últimos X bytes.

Este (e o corte) são portáteis.

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

este script fornece o número exato de caracteres da linha e localização específica, por exemplo:

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

dá os caracteres na linha 5 e os caracteres 5 a 8 da linha 5,

Nota : tail -1é usado para selecionar a última linha exibida pelo cabeçote.

Vignesh
fonte
2

você também pode cortar a linha e cortá-la como, por exemplo:

nome do arquivo grep 'text' | cut -c 1-5

nkr1pt
fonte
Isso não funciona se o arquivo de entrada for um fluxo infinito sem \ ns
Ajay Brahmakshatriya
2

Eu sei que a resposta é em resposta a uma pergunta feita 6 anos atrás ...

Mas eu estava procurando por algo semelhante por algumas horas e então descobri que: cut -c faz exatamente isso, com um bônus adicional de que você também pode especificar um deslocamento.

cut -c 1-5 retornará Hello e cut -c 7-11 retornará world . Não há necessidade de nenhum outro comando

bobbyus
fonte
2
Seu direito !. Eu só queria destacar a possibilidade de um comando único mais genérico que pode retornar texto do meio de um arquivo, ao contrário de head -c lerá apenas os caracteres iniciais, tail -c os últimos caracteres. E sem usar grep :).
bobbyus
2

Mesmo que isso tenha sido respondido / aceito anos atrás, a resposta atualmente aceita é correta apenas para codificações de um byte por caractere como iso-8859-1, ou para subconjuntos de byte único de conjuntos de caracteres de bytes variáveis ​​(como caracteres latinos em UTF-8). Mesmo usando emendas de bytes múltiplos, em vez disso, ainda funcionaria apenas para codificações de vários bytes fixos como UTF-16. Dado que agora o UTF-8 está a caminho de ser um padrão universal, e ao olhar para esta lista de idiomas por número de falantes nativos e esta lista dos 30 principais idiomas por uso nativo / secundário , é importante apontar um Técnica simples de caracteres de byte variável (não baseada em bytes), usando cut -ce tr/ sedcom classes de caracteres.

Compare o seguinte, que falha duplamente devido a dois erros / presunções centradas no latim comuns em relação ao problema de bytes vs. caracteres (um é headvs. cut, o outro é [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]]

para isso (nota: isto funcionou bem no FreeBSD, mas ambos cut& tron GNU / Linux ainda mutilado grega em UTF-8 para me embora):

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

Outra resposta mais recente já havia proposto "cortar", mas apenas por causa do problema secundário que pode ser usado para especificar deslocamentos arbitrários, não por causa do problema de caractere x bytes diretamente relevante.

Se o seu cutnão lidar -ccom codificações de bytes variáveis ​​corretamente, para "os primeiros Xcaracteres" (substitua Xpelo seu número), você pode tentar:

  • sed -E -e '1 s/^(.{X}).*$/\1/' -e q - que se limita à primeira linha
  • head -n 1 | grep -E -o '^.{X}' - que é limitado à primeira linha e encadeia dois comandos embora
  • dd - o que já foi sugerido em outras respostas, mas é muito complicado
  • Um sedscript complicado com buffer de janela deslizante para lidar com caracteres espalhados por várias linhas, mas isso é provavelmente mais complicado / frágil do que apenas usar algo comodd

Se o seu trnão lidar com classes de caracteres com codificações de bytes variáveis ​​corretamente, você pode tentar:

  • sed -E -e 's/[[:upper:]]/\L&/g (Específico do GNU)
Rowanthorpe
fonte
desculpe, mas não funciona aqui ... printf 'Πού ' | cut -c 1só retorna rabiscos ... se comporta como 'cabeça'
LEo
de acordo com a documentação on-line, ainda não está disponível: "Selecione para imprimir apenas os caracteres nas posições listadas na lista de caracteres. O mesmo que -b por enquanto, mas a internacionalização vai mudar isso." [ gnu.org/software/coreutils/manual/html_node/…
LEo
@LEo Com base no link em seu segundo comentário, parece que você está usando um sistema operacional baseado em GNU, presumivelmente GNU / Linux, então, nesse caso, é o esperado - menciono isso no final da minha resposta. Funcionou então (e funciona agora) para mim no FreeBSD (e provavelmente em alguns outros sistemas operacionais), mas não (e ainda não funciona) no GNU / Linux, para esse caso, mencionei os métodos alternativos no final. Eu pessoalmente não posso esperar até que alguém encontre e ofereça tempo livre para fazer a internacionalização necessária para que o conjunto de ferramentas GNU funcione tão bem quanto os outros a esse respeito.
Rowanthorpe
0

Aqui está um script simples que termina usando a ddabordagem mencionada aqui:

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
fonte