Comando Linux (come cat) per leggere una quantità specificata di caratteri


120

Esiste un comando come catin Linux che può restituire una quantità specificata di caratteri da un file?

ad esempio, ho un file di testo come:

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

E voglio qualcosa che restituisca i primi 5 caratteri, che sarebbe "ciao".

Grazie


Si noti che nessuna delle risposte fornite consuma solo N byte da un flusso. Ad esempio: mkfifo /tmp/test.fifo; echo "hello world">/tmp/test.fifo & head -c 5 /tmp/test.fifoconsuma anche " world\n"che si perde per sempre.
Yeti

Risposte:


192

head funziona anche:

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

.. estrarrà i primi 100 byte e li restituirà.

La cosa bella dell'uso headper questo è che la sintassi per le tailcorrispondenze:

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

Puoi combinarli per ottenere intervalli di byte. Ad esempio, per ottenere i secondi 100 byte da un file, leggi i primi 200 con heade usa tail per ottenere gli ultimi 100:

head -c 200 file | tail -c 100

@ Miffy: leggi i primi 20 byte con head, quindi usa tailper ottenere gli ultimi 10, ad esempio:head -c 20 file | tail -c 10
Dan

47

È possibile utilizzare dd per estrarre blocchi arbitrari di byte.

Per esempio,

dd skip=1234 count=5 bs=1

copierebbe i byte da 1235 a 1239 dal suo input al suo output e scarterebbe il resto.

Per ottenere solo i primi cinque byte dallo standard input, eseguire:

dd count=5 bs=1

Nota che, se vuoi specificare il nome del file di input, dd ha un'analisi degli argomenti vecchio stile, quindi dovresti fare:

dd count=5 bs=1 if=filename

Nota anche che dd annuncia verbalmente quello che ha fatto, quindi per buttarlo via, fai:

dd count=5 bs=1 2>&-

o

dd count=5 bs=1 2>/dev/null

2
Raccomanderei questa soluzione in generale, poiché dd bs=1costringe dd a leggere e scrivere un singolo carattere alla volta, il che è molto più lento di headquando il conteggio è grande. Tuttavia, non si nota per count = 5.
effimero

2
Che dire di "dd count = 1 bs = 5"? Ciò farebbe leggere cinque byte in una volta sola. Tuttavia, la testa è probabilmente una soluzione più chiara.
Ben Combee,

1
Grazie per questo - in effetti stavo cercando un modo per "tagliare" un file binario e dd, a quanto pare, farà il trucco .. Salute!
sdaau

questo è stato un head -cdd bs=5 count=1
vero toccasana

11

testa :

Nome

head - visualizza la prima parte dei file

Sinossi

head [ OPZIONE ] ... [ FILE ] ...

Descrizione

Stampa le prime 10 righe di ogni FILE sullo standard output. Con più di un FILE, far precedere ciascuno da un'intestazione che dia il nome del file. Senza FILE, o quando FILE è -, leggi lo standard input.

Gli argomenti obbligatori per le opzioni lunghe sono obbligatori anche per le opzioni brevi.
-c , --bytes = [-] N stampa i primi N byte di ogni file; con il '-' iniziale, stampa tutti tranne gli ultimi N byte di ogni file


3

anche la testa o la coda possono farlo:

testa -c X

Stampa i primi X byte (non necessariamente caratteri se si tratta di un file UTF-16) del file. tail farà lo stesso, tranne che per gli ultimi X byte.

Questo (e il taglio) sono portatili.


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

questo script fornisce il numero esatto di caratteri dalla riga e dalla posizione specifiche, ad esempio:

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

fornisce i caratteri nella riga 5 e i caratteri da 5 a 8 della riga 5,

Nota : tail -1serve per selezionare l'ultima riga visualizzata dalla testa.


2

potresti anche estrarre la linea e poi tagliarla come ad esempio:

nomefile grep 'text' | tagliare -c 1-5


Questo non funziona se il file di input è un flusso infinito senza \ n
Ajay Brahmakshatriya

2

So che la risposta è in risposta a una domanda posta 6 anni fa ...

Ma stavo cercando qualcosa di simile per alcune ore e poi ho scoperto che: cut -c fa esattamente questo, con un ulteriore vantaggio che potresti anche specificare un offset.

cut -c 1-5 restituirà Hello e cut -c 7-11 restituirà world . Non c'è bisogno di altri comandi


2
Destra!. Volevo solo evidenziare la possibilità di un singolo comando più generico che può restituire del testo dal centro di un file a differenza di head -c leggerà solo i caratteri iniziali, tail -c gli ultimi caratteri. E senza usare grep :).
bobbyus

2

Anche se questo è stato risposto / accettato anni fa, la risposta attualmente accettata è corretta solo per le codifiche di un byte per carattere come iso-8859-1, o per i sottoinsiemi a byte singolo di set di caratteri a byte variabile (come i caratteri latini all'interno di UTF-8). Anche l'utilizzo di giunzioni a più byte invece funzionerebbe ancora solo per codifiche multibyte fisse come UTF-16. Dato che ora UTF-8 è sulla buona strada per diventare uno standard universale, e quando si guarda questo elenco di lingue per numero di madrelingua e questo elenco delle prime 30 lingue per utilizzo nativo / secondario , è importante sottolineare un semplice tecnica a byte variabile (non basata su byte), che utilizza cut -ce tr/ sedcon classi di caratteri.

Confronta quanto segue che doppiamente fallisce a causa di due comuni errori / presunzioni latini riguardanti il ​​problema dei byte rispetto ai caratteri (uno è headcontro cut, l'altro è [a-z][A-Z]contro [: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]]

a questo (nota: questo ha funzionato bene su FreeBSD, ma entrambi cute trsu GNU / Linux hanno ancora alterato il greco in UTF-8 per me):

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

Un'altra risposta più recente aveva già proposto il "taglio", ma solo per il problema secondario che può essere utilizzato per specificare offset arbitrari, non per il problema direttamente rilevante tra caratteri e byte.

Se cutnon -cgestisci correttamente le codifiche a byte variabile, per "i primi Xcaratteri" (sostituisci Xcon il tuo numero) potresti provare:

  • sed -E -e '1 s/^(.{X}).*$/\1/' -e q - che è però limitato alla prima riga
  • head -n 1 | grep -E -o '^.{X}' - che è limitato alla prima riga e concatena due comandi
  • dd - che è già stato suggerito in altre risposte, ma è davvero macchinoso
  • Uno sedscript complicato con buffer della finestra scorrevole per gestire i caratteri distribuiti su più righe, ma probabilmente è più ingombrante / fragile del semplice utilizzo di qualcosa di similedd

Se trnon gestisci correttamente le classi di caratteri con codifiche a byte variabili, potresti provare:

  • sed -E -e 's/[[:upper:]]/\L&/g (GNU-specifico)

scusa, ma qui non funziona ... printf 'Πού ' | cut -c 1restituisce solo
parole

secondo la documentazione in linea, non è ancora disponibile: "Selezionare per stampare solo i caratteri nelle posizioni elencate nell'elenco dei caratteri. Come per ora, lo stesso di -b, ma l'internazionalizzazione cambierà questo." [ gnu.org/software/coreutils/manual/html_node/…
LEo

@LEo Sulla base del collegamento nel tuo secondo commento sembra che tu stia utilizzando un sistema operativo basato su GNU, presumibilmente GNU / Linux, quindi in tal caso è previsto - lo menziono alla fine della mia risposta. Allora funzionava (e ora funziona) per me su FreeBSD (e probabilmente su altri sistemi operativi) ma non (e non ancora) funzionava su GNU / Linux, per quel caso ho menzionato i metodi alternativi alla fine. Personalmente non vedo l'ora che qualcuno trovi e offra volontariamente il tempo libero per fare l'internazionalizzazione necessaria affinché il set di strumenti GNU funzioni come gli altri sotto questo aspetto.
Rowanthorpe

0

Ecco un semplice script che si conclude utilizzando l' ddapproccio menzionato qui:

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
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.