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.