obter os primeiros caracteres X do comando cat?

42

Eu tenho um arquivo de texto que estou produzindo para uma variável no meu script de shell. Só preciso dos 50 primeiros caracteres.

Eu tentei usar, cat ${filename} cut -c1-50mas estou ficando muito mais do que os 50 primeiros caracteres? Isso pode dever-se à cutprocura de linhas (sem 100% de certeza), enquanto esse arquivo de texto pode ser uma cadeia longa - realmente depende.

Existe um utilitário por aí que eu possa usar para obter os primeiros caracteres X de um catcomando?

jkj2000
fonte
10
Você esqueceu um |? cat ${filename} | cut -c1-50
DisplayName
@DisplayName corrigido, obrigado por capturar meu erro de redigitação.
jkj2000
1
@ jkj2000, voltei à versão anterior, pois essa era a pergunta original.
Ramesh Ramesh

Respostas:

61
head -c 50 file

Isso retorna os primeiros 50 bytes.

Lembre-se de que o comando nem sempre é implementado da mesma forma em todos os sistemas operacionais. No Linux e macOS, ele se comporta dessa maneira. No Solaris (11), você precisa usar a versão gnu em / usr / gnu / bin /

Mostrar nome
fonte
cabeça não tem -copção. Eu iria para dd (1) em vez disso.
mirabilos
7
Observe que esta resposta supõe que o arquivo contenha apenas caracteres ASCII, como o OP solicitou os primeiros caracteres X, não bytes.
Calimo 14/11
2
@mirabilos Pode não ser portátil, mas a minha versão ( GNU coreutils 5.97) sim.
Yossarian 14/11
1
O POSIX não define -ccomo uma opção válida, no entanto, portanto, é definitivamente dependente do seu ambiente local. unix.com/man-page/posix/1/head
Jules
1
@ Calimo Sim, eu sei, mas tentei criar um arquivo de texto com 100 caracteres e executar o meu comando e ele imprimiu 50 caracteres. Mas você está certo sobre o ASCII, mas como o OP sinalizou isso como respondido, não havia nenhum no caso dele.
DisplayName
27

Seu cutcomando funcionará se você usar um canal para passar dados para ele:

cat ${file} | cut -c1-50 

Ou, evitando um uso inútil do gato e tornando-o um pouco mais seguro:

cut -c1-50 < "$file"

Observe que os comandos acima imprimirão os primeiros 50 caracteres (ou bytes, dependendo da sua cutimplementação) de cada linha de entrada . Ele deve fazer o que você espera se, como você diz, seu arquivo for uma linha enorme.

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

Isso retorna os primeiros 50 bytes.

doneal24
fonte
dd não tem status=nonebandeira. Use em 2>/dev/nullvez disso (e cite corretamente): dd if="$filename" bs=1 count=50 2>/dev/null(mesmo assim, considere usar bs=50 count=1para reduzir o número de syscalls envolvidos).
mirabilos
1
O @mirabilos dd possui status=noneao usar o Ubuntu 14.04, coreutils 8.21, mas você está certo em usar 2>/dev/nulluma versão anterior.
doneal24
1
@mirabilos A maioria das distribuições Linux usa o GNU coreutils, assim como o FreeBSD e outros BSDs. Está disponível no Solaris como pacote gnu-coreutils. Sim, este é "Unix & Linux" e os sistemas Unix e Linux usam o GNU coreutils.
doneal24
2
Não, os sistemas Unix geralmente não usam utilitários GNU. GNU é um acrônimo para "GNU não é Unix", até. Por favor, atenha-se às soluções portáteis ou, se precisar fornecer soluções apenas para GNU, declare-o e, se possível, mostre uma solução portátil equivalente.
mirabilos
1
A rigor, esse é um read()dos 50 bytes. Se filefor um canal, por exemplo, e menos caracteres estiverem disponíveis no momento, menos bytes serão retornados. Para ter o equivalente a head -c50, você precisaria usar o específico do GNU iflag=fullblock.
Stéphane Chazelas
4

Até agora, a maioria das respostas assume que 1 byte = 1 caractere, o que pode não ser o caso se você estiver usando um código de idioma não ASCII.

Uma maneira um pouco mais robusta de fazer isso:

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

Observe que isso pressupõe:

  1. Você está usando ksh93, bash(ou uma recente zshou mksh(embora o único charset multi-byte apoiado por mkshé UTF-8 e só depois set -o utf8-mode)) e uma versão de headque os suportes -c(a maioria faz hoje em dia, mas não estritamente standard).
  2. O código do idioma atual é definido com a mesma codificação que o arquivo (digite locale charmape file -- "$filename"verifique isso); caso contrário, defina-o com ie. LC_ALL=en_US.UTF-8)
  3. Levei os primeiros 200 bytes do arquivo head, assumindo o pior caso UTF-8, em que todos os caracteres são codificados com no máximo 4 bytes. Isso deve cobrir a maioria dos casos em que consigo pensar.
Calimo
fonte
Obviamente, isso também pressupõe o GNU head, ou outra implementação do mesmo que adiciona a -copção não padrão . Mas você já está exigindo o GNU bash. (Nota: mksho modo UTF-8 do pode fazer isso para arquivos codificados em UTF-8.) Gostaria de perguntar ao OP se eles exigem caracteres octetos ou multibyte, apenas "caracteres" é um termo vago / genérico.
mirabilos
Isso também pressupõe $filenameou $testStringnão contém nova linha ou curingas em branco ou para começar -.
Stéphane Chazelas
A ${var:offset:length}construção que você está usando aqui é realmente originária ksh93e também é suportada por versões recentes de zsh( zshpossui sua própria $testString[1,50]). Você precisa ${testString:0:50} no ksh93e zshno entanto.
Stéphane Chazelas
Acabei de editar minha resposta para endereçar os comentários acima
Calimo 06/04
2
grep -om1 "^.\{50\}" ${filename}

Outra variante (para a primeira linha do arquivo)

(IFS= read -r line <${filename}; echo ${line:0:50})
Costas
fonte
Trata-se de abuso de ferramentas de alto nível - e propenso a não fazer o que você deseja, por exemplo, se elas reconhecem a localidade.
mirabilos
@mirabilos O que você quer dizer com ferramentas de alto nível : reade echo? Ou bash expansion?
Costas
grep(regexp) e sim, o uso de shell aqui (dica: a primeira linha pode ser grande). (Dito isto, o Bashismo também não está na POSIX, mas a maioria das conchas implementar isso.)
mirabilos
0

1. Para arquivos ASCII, faça como @DisplayName diz:

head -c 50 file.txt

imprimirá os primeiros 50 caracteres do arquivo.txt, por exemplo.

2. Para dados binários, use-os hexdumppara imprimi-los como caracteres hexadecimais:

hexdump -n 50 -v file.bin

imprimirá os primeiros 50 bytes de file.bin, por exemplo.

Observe que, sem a -vopção detalhada, hexdumpsubstituiria linhas repetidas por um asterisco ( *). Consulte aqui: https://superuser.com/questions/494245/what-does-an-asterisk-mean-in-hexdump-output/494613#494613 .

Gabriel Staples
fonte
-2

Você pode usar o sed para isso, que resolverá o problema com bastante facilidade

sed -e 's/^\(.\{50\}\).*/\1/' yourfile
munkeyoto
fonte
Curioso para saber como isso foi downvoted se ele resolve a pergunta do OP: "Eu só preciso dos primeiros 50 caracteres" Isto consegue o que foi solicitado, sem UUOC (Useless Uso de gato)
munkeyoto
1
Esta resposta fornece os primeiros cinquenta caracteres de cada linha do arquivo, não apenas os primeiros 50 do arquivo. Também não imprime nada se todas as linhas tiverem menos de 50 caracteres. Sua solução funcionaria melhor comsed -n -e '1s/^\(.\{50\}\).*/\1/p' ${filename}
doneal24
Entendido poderia ter apenas: cabeça-n 1 | sed -e 's / ^ (. \ {50 \}). * / \ 1 /' ... E isso teria resolvido o problema. OP declarou: "só precisa dos primeiros 50 caracteres"
munkeyoto
1
Não. Se a primeira linha tiver apenas 49 caracteres, não produzirá nada.
doneal24
Doug, eu entendi isso pela primeira vez, mas o OP não mencionou nada sobre impressão se a linha contivesse menos de 50 caracteres, por isso ainda não entendi o seu ponto, nem o ponto disso ter sido rebaixado desde que caiu no que teria funcionado. head: head -n 1 $ {nome do arquivo} | sed -n -e '1s / ^ (\ {50 \}.) * / \ 1 / p.'
munkeyoto