ottenere i primi X caratteri dal comando cat?


42

Ho un file di testo che sto inviando a una variabile nel mio script di shell. Ho solo bisogno dei primi 50 caratteri.

Ho provato a usare cat ${filename} cut -c1-50ma sto diventando molto più dei primi 50 caratteri? Ciò potrebbe essere dovuto alla cutricerca di righe (non sicuro al 100%), mentre questo file di testo potrebbe essere una stringa lunga - dipende davvero.

C'è un'utilità là fuori in cui posso convogliare per ottenere i primi X caratteri da un catcomando?


10
Hai dimenticato un |? cat ${filename} | cut -c1-50
— DisplayName

@DisplayName risolto, grazie per aver rilevato il mio errore di riscrittura.
— jkj2000,

1
@ jkj2000, sono tornato alla versione precedente poiché quella era la domanda originale.
— Ramesh,

Risposte:


61
head -c 50 file

Ciò restituisce i primi 50 byte.

Ricorda che il comando non è sempre implementato allo stesso modo su tutti i sistemi operativi. Su Linux e macOS si comporta in questo modo. Su Solaris (11) è necessario utilizzare la versione gnu in / usr / gnu / bin /


la testa non ha -copzione. Preferirei invece dd (1) .
— mirabilos,

7
Si noti che questa risposta presuppone che il file contenga solo caratteri ASCII, poiché l'OP ha richiesto i primi X caratteri, non i byte.
— Calimo,

2
@mirabilos Potrebbe non essere portatile, ma la mia versione ( GNU coreutils 5.97) lo fa.
— Yossarian

1
POSIX non definisce -cun'opzione valida, tuttavia, quindi dipende sicuramente dal tuo ambiente locale. unix.com/man-page/posix/1/head
— Jules

1
@Calimo Sì, lo so, ma ho provato a creare un file di testo con 100 caratteri, quindi ho eseguito il mio comando e ha stampato 50 caratteri. Ma hai ragione su ASCII, ma dal momento che OP ha segnalato questo come risposta non ce n'era nessuno nel suo caso.
— DisplayName

27

Il tuo cutcomando funziona se usi una pipe per passarci i dati:

cat ${file} | cut -c1-50 

Oppure, evitando un uso inutile del gatto e rendendolo un po 'più sicuro:

cut -c1-50 < "$file"

Si noti che i comandi precedenti stamperanno i primi 50 caratteri (o byte, a seconda cutdell'implementazione) di ciascuna riga di input . Dovrebbe fare quello che ti aspetti se, come dici, il tuo file è una linea enorme.


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

Ciò restituisce i primi 50 byte.


dd non ha status=noneflag. Usa 2>/dev/nullinvece (e cita correttamente): dd if="$filename" bs=1 count=50 2>/dev/null(anche così, considera l'utilizzo bs=50 count=1per ridurre il numero di syscall coinvolti).
— mirabilos,

1
@mirabilos dd ha status=nonequando si utilizza Ubuntu 14.04, coreutils 8.21, ma è giusto usare 2>/dev/nullse si utilizza una versione precedente.
— doneal24,

1
@mirabilos La maggior parte delle distribuzioni Linux usano coreutils GNU come FreeBSD e altri BSD. È disponibile su Solaris come pacchetto gnu-coreutils. Sì, questo è "Unix e Linux" e entrambi i sistemi Unix e Linux usano coreutils GNU.
— doneal24,

2
No, i sistemi Unix non usano generalmente utility GNU. GNU è anche l'acronimo di "GNU is not Unix". Attenersi alle soluzioni portatili o, se è necessario fornire soluzioni solo per GNU, dichiararlo e, se possibile, mostrare una soluzione portatile equivalente.
— mirabilos,

1
A rigor di termini, quello fa uno read()di 50 byte. Se fileper esempio è una pipe e sono disponibili meno caratteri alla volta, verranno restituiti meno byte. Per avere l'equivalente di head -c50, dovresti usare lo specifico GNU iflag=fullblock.
— Stéphane Chazelas,

4

La maggior parte delle risposte finora presuppone che 1 byte = 1 carattere, il che potrebbe non essere il caso se si utilizza una locale non ASCII.

Un modo leggermente più robusto per farlo:

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

Si noti che ciò presuppone:

  1. Stai utilizzando ksh93, bash(o un recente zsho mksh(anche se l'unico set di caratteri multibyte supportato da mkshUTF-8 e solo dopo set -o utf8-mode)) e una versione di headtale supporto -c(la maggior parte lo fa al giorno d'oggi, ma non strettamente standard).
  2. La locale corrente è impostata sulla stessa codifica del file (digitare locale charmape file -- "$filename"per controllarlo); in caso contrario, impostarlo con ie. LC_ALL=en_US.UTF-8)
  3. Ho preso i primi 200 byte del file head, assumendo l'UTF-8 nel peggiore dei casi in cui tutti i caratteri sono codificati al massimo su 4 byte. Questo dovrebbe coprire la maggior parte dei casi che mi vengono in mente.

Naturalmente, questo presuppone anche GNU head, o un'altra implementazione di esso che aggiunge l' -copzione nōn standard . Ma hai già bisogno di GNU bash. (Nota: mkshla modalità UTF-8 potrebbe farlo per i file codificati UTF-8.) Chiederei all'OP se richiedono ottetti o caratteri multibyte, solo "caratteri" è un termine vago / gernico.
— mirabilos,

Ciò presuppone anche $filenameo $testStringnon contiene nuova riga vuota o caratteri jolly o iniziare con -.
— Stéphane Chazelas,

Il ${var:offset:length}costrutto che stai usando qui proviene effettivamente ksh93ed è supportato anche dalle versioni recenti di zsh( zshha il suo $testString[1,50]). Hai bisogno ${testString:0:50} di ksh93e zshcomunque.
— Stéphane Chazelas,

Ho appena modificato la mia risposta per rispondere ai commenti sopra
— Calimo,

2
grep -om1 "^.\{50\}" ${filename}

Altra variante (per la prima riga nel file)

(IFS= read -r line <${filename}; echo ${line:0:50})

Questo è un abuso di strumenti di alto livello - e incline a non fare ciò che vuoi, ad esempio se sono a conoscenza delle impostazioni locali.
— mirabilos,

@mirabilos Cosa intendi con strumenti di alto livello : reade echo? Oppure bash expansion?
— Costas,

grep(regexp) e sì, l'uso della shell qui (suggerimento: la prima riga potrebbe essere grande). (Detto questo, anche il bashismo non è in POSIX, ma la maggior parte delle conchiglie lo implementa.)
— mirabilos

0

1. Per i file ASCII, fai come dice @DisplayName:

head -c 50 file.txt

stamperà i primi 50 caratteri di file.txt, ad esempio.

2. Per i dati binari, utilizzare hexdumpper stamparli come caratteri esadecimali:

hexdump -n 50 -v file.bin

stamperà i primi 50 byte di file.bin, per esempio.

Si noti che senza l' -vopzione verbose, hexdumpsostituirebbe le righe ripetute con un asterisco ( *). Vedi qui: https://superuser.com/questions/494245/what-does-an-asterisk-mean-in-hexdump-output/494613#494613 .


-2

Puoi usare sed per questo che affronterà il problema abbastanza facilmente

sed -e 's/^\(.\{50\}\).*/\1/' yourfile

Curioso di sapere come questo è stato declassato se risolve la domanda del PO: "Ho solo bisogno dei primi 50 caratteri" Questo compie ciò che è stato richiesto senza UUOC (Useless Use of Cat)
— munkeyoto

1
Questa risposta fornisce i primi cinquanta caratteri di ogni riga del file, non solo i primi 50 del file. Inoltre, non stampa nulla se tutte le righe sono lunghe meno di 50 caratteri. La tua soluzione funzionerebbe meglio consed -n -e '1s/^\(.\{50\}\).*/\1/p' ${filename}
— doneal24

Capito avrebbe potuto solo: head -n 1 | sed -e 's / ^ (. \ {50 \}). * / \ 1 /' ... E avrebbe risolto il problema. OP dichiarò: "servono solo i primi 50 caratteri"
— munkeyoto,

1
No. Se la prima riga fosse lunga solo 49 caratteri, non produrrebbe nulla.
— doneal24,

Doug L'ho capito la prima volta, eppure l'OP non ha menzionato nulla sulla stampa se la linea contenesse meno di 50 caratteri, quindi non riesco ancora a vedere il tuo punto, né il punto di questo è stato sottratto poiché di nuovo è caduto in quello con cui avrebbe funzionato head: head -n 1 $ {nome file} | sed -n -e '1s / ^ (. \ {50 \}). * / \ 1 / p'
— munkeyoto
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.