obtenir les premiers X caractères de la commande cat?

42

J'ai un fichier texte que je sors en sortie d'une variable dans mon script shell. Cependant, je n'ai besoin que des 50 premiers caractères.

J'ai essayé d'utiliser cat ${filename} cut -c1-50mais j'obtiens beaucoup plus que les 50 premiers caractères? Cela peut être dû à la cutrecherche de lignes (pas à 100% sûres), alors que ce fichier texte pourrait être une longue chaîne - cela dépend vraiment.

Existe-t-il un utilitaire dans lequel je puisse accéder pour obtenir les X premiers caractères d'une catcommande?

jkj2000
la source
10
Vous avez oublié un |? cat ${filename} | cut -c1-50
DisplayName
@DisplayName corrigé, merci d'avoir détecté mon erreur de saisie.
Jkj2000
1
@ jkj2000, je suis revenu à l'ancienne version car c'était la question initiale.
Ramesh

Réponses:

61
head -c 50 file

Cela retourne les 50 premiers octets.

Notez que la commande n'est pas toujours implémentée de la même manière sur tous les systèmes d'exploitation. Sous Linux et macOS, il se comporte de cette façon. Sous Solaris (11), vous devez utiliser la version gnu de / usr / gnu / bin /.

Afficher un nom
la source
la tête n'a pas d' -coption. Je choisirais plutôt dd (1) .
mirabilos
7
Notez que cette réponse suppose que le fichier ne contient que des caractères ASCII, car l'OP a demandé les premiers caractères X, et non des octets.
Calimo
2
@mirabilos Il n'est peut-être pas portable, mais ma version ( GNU coreutils 5.97) le fait.
Yossarian
1
POSIX ne définit pas -ccomme une option valide, cependant, il dépend donc de votre environnement local. unix.com/man-page/posix/1/head
Jules
1
@Calimo Oui, je sais, mais j'ai essayé de créer un fichier texte de 100 caractères, puis d'exécuter ma commande et de l'afficher à 50 caractères. Mais vous avez raison pour ASCII, mais puisque OP a signalé cela comme une réponse, il n'y en avait pas dans son cas.
DisplayName
27

Votre cutcommande fonctionne si vous utilisez un canal pour lui transmettre des données:

cat ${file} | cut -c1-50 

Ou, en évitant une utilisation inutile du chat et en le rendant un peu plus sûr:

cut -c1-50 < "$file"

Notez que les commandes ci-dessus imprimeront les 50 premiers caractères (ou octets, selon votre cutimplémentation) de chaque ligne d'entrée . Il convient de faire ce que vous attendez si, comme vous le dites, votre fichier est une énorme ligne.

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

Cela retourne les 50 premiers octets.

doneal24
la source
dd n'a pas d' status=noneindicateur. Utilisez 2>/dev/nullplutôt (et citez correctement): dd if="$filename" bs=1 count=50 2>/dev/null(même si, envisagez d'utiliser bs=50 count=1pour réduire le nombre d'appels système impliqués).
mirabilos
1
@mirabilos dd a status=nonequand utiliser Ubuntu 14.04, coreutils 8.21, mais vous avez le droit de l'utiliser 2>/dev/nullsi vous utilisez une version antérieure.
doneal24
1
@mirabilos La plupart des distributions Linux utilisent GNU coreutils comme le font FreeBSD et d'autres BSD. Il est disponible sur Solaris sous la forme du package gnu-coreutils. Oui, il s’agit de "Unix & Linux" et les systèmes Unix et Linux utilisent GNU coreutils.
doneal24
2
Non, les systèmes Unix n'utilisent généralement pas les utilitaires GNU. GNU est un acronyme pour «GNU is not Unix», même. Veuillez vous en tenir aux solutions portables ou, si vous devez fournir des solutions exclusivement GNU, indiquez-le et, si possible, montrez une solution portable équivalente.
mirabilos
1
Strictement parlant, cela fait l'un read()des 50 octets. Par exemple, s'il files'agit d'un canal et que moins de caractères sont disponibles, moins d'octets seront renvoyés. Pour avoir l’équivalent de head -c50, vous devez utiliser la spécificité GNU iflag=fullblock.
Stéphane Chazelas
4

Jusqu'à présent, la plupart des réponses supposent qu'un caractère sur 1 octet = 1, ce qui peut ne pas être le cas si vous utilisez des paramètres régionaux non-ASCII.

Une façon un peu plus robuste de le faire:

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

Notez que cela suppose:

  1. Vous utilisez ksh93, bash(ou un récent zshou mksh(bien que le seul jeu de caractères multi-octets pris en charge par mkshest UTF-8 et seulement après set -o utf8-mode)) et une version headque les supports -c( la plupart de nos jours, mais pas strictement standard).
  2. Les paramètres régionaux actuels sont définis sur le même encodage que le fichier (tapez locale charmapet file -- "$filename"vérifiez-le); sinon, définissez-le avec c.-à-d. LC_ALL=en_US.UTF-8)
  3. J'ai pris les 200 premiers octets du fichier avec head, en supposant le cas UTF-8 dans le cas le plus défavorable, où tous les caractères sont codés sur au plus 4 octets. Cela devrait couvrir la plupart des cas auxquels je peux penser.
Calimo
la source
Bien entendu, cela suppose également GNU head, ou une autre de ses implémentations, qui ajoute l’ -coption nōn-standard . Mais vous avez déjà besoin de GNU bash. (Remarque: mkshle mode UTF-8 pourrait faire cela pour les fichiers encodés en UTF-8.) Je demanderais à l'OP s'ils ont besoin d'octets ou de caractères multi-octets; simplement, "caractères" est un terme vague / générique.
mirabilos
Cela suppose également $filenameou $testStringne contient pas de nouvelle ligne vierge, de caractère générique ou de début -.
Stéphane Chazelas
La ${var:offset:length}construction que vous utilisez ici provient en fait de ksh93et est également prise en charge par les versions récentes de zsh( zshpossède la sienne $testString[1,50]). Vous avez besoin ${testString:0:50} de ksh93et zshcependant.
Stéphane Chazelas
Je
viens de modifier
2
grep -om1 "^.\{50\}" ${filename}

Autre variante (pour la première ligne du fichier)

(IFS= read -r line <${filename}; echo ${line:0:50})
Costas
la source
C'est un abus d'outils de haut niveau - et enclin à ne pas faire ce que vous voulez, par exemple s'ils sont sensibles aux paramètres régionaux.
mirabilos
@mirabilos Que voulez-vous dire par outils de haut niveau : readet echo? Ou bash expansion?
Costas
grep(regexp), et oui, l'utilisation de shell ici (indice: la première ligne peut être grande). (Cela étant dit, le bashisme n'est pas non plus dans POSIX, mais la plupart des obus implémentent cela.)
mirabilos
0

1. Pour les fichiers ASCII, faites comme @DisplayName dit:

head -c 50 file.txt

va imprimer les 50 premiers caractères de file.txt, par exemple.

2. Pour les données binaires, utilisez-les hexdumppour les imprimer en caractères hexadécimaux:

hexdump -n 50 -v file.bin

va imprimer les 50 premiers octets de file.bin, par exemple.

Notez que sans l' -voption verbose, hexdumples lignes répétées seraient remplacées par un astérisque ( *). Voir ici: https://superuser.com/questions/494245/what-does-an-asterisk-mean-in-hexdump-output/494613#494613 .

Gabriel Staples
la source
-2

Vous pouvez utiliser sed pour cela, ce qui résoudra le problème assez facilement

sed -e 's/^\(.\{50\}\).*/\1/' yourfile
Munkeyoto
la source
Curieux de savoir comment cela a downvoted si elle résout la question du OP: « Je ne ai besoin les 50 premiers caractères » Ce accomplit ce qui a été demandé sans UUOC (inutile l' utilisation de Cat)
munkeyoto
1
Cette réponse donne les cinquante premiers caractères de chaque ligne du fichier, pas seulement les 50 premiers du fichier. Aussi, n'imprime rien du tout si toutes les lignes ont moins de 50 caractères. Votre solution fonctionnerait mieux avecsed -n -e '1s/^\(.\{50\}\).*/\1/p' ${filename}
doneal24
Compris aurait pu simplement: tête -n 1 | sed -e 's / ^ (. \ {50 \}). * / \ 1 /' ... Et le problème aurait été résolu. OP a déclaré: "seulement besoin des 50 premiers caractères"
munkeyoto
1
Nan. Si la première ligne ne compte que 49 caractères, rien ne sera généré.
doneal24
Doug, je l'ai compris la première fois, mais le PO n'a rien dit sur l'impression si la ligne contenait moins de 50 caractères. Je ne vois toujours pas votre point de vue, ni le fait que cela ait été annulé car il est à nouveau tombé dans ce qui aurait fonctionné avec. head: head -n 1 $ {nom du fichier} | sed -n -e '1s / ^ (. \ {50 \}). * / \ 1 / p'
munkeyoto