Polecenie Linuksa (jak cat) do odczytania określonej ilości znaków


120

Czy istnieje polecenie jak catw Linuksie, które może zwrócić określoną ilość znaków z pliku?

np. mam plik tekstowy taki jak:

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

I chcę czegoś, co zwróci pierwsze 5 znaków, czyli „cześć”.

dzięki


Zwróć uwagę, że żadna z podanych odpowiedzi nie zużywa tylko N bajtów ze strumienia. Na przykład: mkfifo /tmp/test.fifo; echo "hello world">/tmp/test.fifo & head -c 5 /tmp/test.fiforównież konsumuje, " world\n"co jest stracone na zawsze.
Yeti

Odpowiedzi:


192

head działa też:

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

..wyodrębni pierwsze 100 bajtów i zwróci je.

Przyjemne w użyciu headdo tego jest to, że składnia taildopasowań:

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

Możesz je łączyć, aby uzyskać zakresy bajtów. Na przykład, aby pobrać drugie 100 bajtów z pliku, przeczytaj pierwsze 200 headi użyj tail, aby uzyskać ostatnie 100:

head -c 200 file | tail -c 100

@Miffy: Odczytaj pierwsze 20 bajtów za pomocą head, a następnie użyj, tailaby uzyskać ostatnie 10, np .:head -c 20 file | tail -c 10
Dan

47

Możesz użyć dd do wyodrębnienia dowolnych fragmentów bajtów.

Na przykład,

dd skip=1234 count=5 bs=1

skopiowałby bajty od 1235 do 1239 ze swojego wejścia na wyjście, a resztę odrzucił.

Aby po prostu pobrać pierwsze pięć bajtów ze standardowego wejścia, wykonaj:

dd count=5 bs=1

Zauważ, że jeśli chcesz określić nazwę pliku wejściowego, dd ma przestarzałe analizowanie argumentów, więc możesz zrobić:

dd count=5 bs=1 if=filename

Zauważ również, że dd werbalnie ogłasza, co zrobił, więc aby to odrzucić, wykonaj:

dd count=5 bs=1 2>&-

lub

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

2
Generalnie odradzałbym to rozwiązanie, ponieważ dd bs=1wymusza na dd odczytywanie i zapisywanie pojedynczego znaku na raz, co jest znacznie wolniejsze niż headwtedy, gdy liczba jest duża. Nie jest to jednak zauważalne, gdy count = 5.
ephemient

2
A co z „dd count = 1 bs = 5”? To oznaczałoby odczytanie pięciu bajtów za jednym razem. Mimo to głowa jest prawdopodobnie jaśniejszym rozwiązaniem.
Ben Combee,

1
Dzięki za to - tak naprawdę szukałem sposobu na „wycięcie” pliku binarnego i ddwygląda na to, że załatwi sprawę… Na zdrowie!
sdaau

to było head -cdd bs=5 count=1
ratunek na busyboxie

11

głowa :

Imię

head - wypisuje pierwszą część plików

Streszczenie

head [ OPCJA ] ... [ PLIK ] ...

Opis

Wydrukuj pierwsze 10 wierszy każdego PLIKU na standardowe wyjście. W przypadku więcej niż jednego PLIKU należy poprzedzić każdy nagłówkiem podającym nazwę pliku. Bez PLIKU lub gdy PLIK ma wartość -, czyta standardowe wejście.

Obowiązkowe argumenty dla długich opcji są również obowiązkowe dla krótkich opcji.
-c , --bytes = [-] N wypisuje pierwsze N ​​bajtów każdego pliku; z początkowym „-” wypisuje wszystkie oprócz ostatnich N bajtów każdego pliku


3

głowa lub ogon również może to zrobić:

głowa -c X

Drukuje pierwsze X bajtów (niekoniecznie znaków, jeśli jest to plik UTF-16) pliku. tail zrobi to samo, z wyjątkiem ostatnich X bajtów.

To (i cięcie) są przenośne.


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

ten skrypt podaje dokładną liczbę znaków z określonej linii i lokalizacji, np .:

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

nadaje znaki w linii 5 i znaki 5 do 8 w linii 5,

Uwaga : tail -1służy do wyboru ostatniej linii wyświetlanej przez głowę.


2

możesz też wyrównać linię, a następnie wyciąć ją jak na przykład:

grep nazwa pliku 'text' | cięcie -c 1-5


To nie działa, jeśli plik wejściowy jest niekończącym się strumieniem bez \ n
Ajay Brahmakshatriya

2

Wiem, że odpowiedź jest odpowiedzią na pytanie zadane 6 lat temu ...

Ale szukałem czegoś podobnego przez kilka godzin, a potem odkryłem, że: cut -c robi dokładnie to, z dodatkową premią, którą można również określić przesunięcie.

cut -c 1-5 powróci Witaj, a cut -c 7-11 powrócą do świata . Nie potrzeba żadnego innego polecenia


2
Masz rację!. Chciałem tylko podkreślić możliwość bardziej ogólnego pojedynczego polecenia, które może zwrócić tekst ze środka pliku, w przeciwieństwie do nagłówka -c będzie czytać tylko znaki początkowe, a koniec -c ostatnie znaki. I bez użycia grepa :).
bobbyus

2

Chociaż odpowiedź na to pytanie została udzielona / zaakceptowana lata temu, obecnie akceptowana odpowiedź jest poprawna tylko dla kodowania jednobajtowego na znak, takiego jak iso-8859-1, lub dla jednobajtowych podzbiorów zestawów znaków o zmiennej bajtach (takich jak znaki łacińskie w ramach UTF-8). Nawet użycie wielobajtowych połączeń zamiast tego nadal działałoby tylko w przypadku stałych wielobajtowych kodowań, takich jak UTF-16. Biorąc pod uwagę, że obecnie UTF-8 jest na dobrej drodze, aby stać się uniwersalnym standardem, i patrząc na tę listę języków według liczby rodzimych użytkowników języka i listę 30 najpopularniejszych języków według użycia rodzimego / wtórnego , należy zwrócić uwagę na prosta technika przyjazna dla znaków (nie oparta na bajtach) ze zmiennymi bajtami, wykorzystująca cut -ci tr/ sedz klasami znaków.

Porównaj następujące, które podwójnie zawodzą z powodu dwóch typowych błędów / przypuszczeń związanych z alfabetem łacińskim dotyczących kwestii bajtów i znaków (jeden jest headprzeciwko cut, drugi jest [a-z][A-Z]przeciwko [: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]]

do tego (uwaga: działało dobrze na FreeBSD, ale w obu cuti trna GNU / Linuksie nadal zniekształcałem grekę w UTF-8):

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

W innej, nowszej odpowiedzi zaproponowano już „wycinanie”, ale tylko ze względu na problem uboczny polegający na tym, że można go użyć do określenia dowolnych przesunięć, a nie z powodu bezpośrednio istotnego problemu znak / bajt.

Jeśli cutnie radzisz sobie -cpoprawnie z kodowaniem ze zmiennymi bajtami, dla „pierwszych Xznaków” (zamień na Xswój numer) możesz spróbować:

  • sed -E -e '1 s/^(.{X}).*$/\1/' -e q - co jest ograniczone do pierwszej linii
  • head -n 1 | grep -E -o '^.{X}' - który jest ograniczony do pierwszej linii i łączy w łańcuchu dwa polecenia
  • dd - co zostało już zaproponowane w innych odpowiedziach, ale jest naprawdę uciążliwe
  • Skomplikowany sedskrypt z buforem przesuwanego okna do obsługi znaków rozłożonych na wiele linii, ale prawdopodobnie jest to bardziej kłopotliwe / delikatne niż zwykłe użycie czegoś takiegodd

Jeśli twój trnie obsługuje poprawnie klas znaków z kodowaniem bajtów zmiennych, możesz spróbować:

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

przepraszam, ale to nie działa tutaj ... printf 'Πού ' | cut -c 1po prostu zwraca bełkot ... zachowuje się jak 'głowa'
LEo

zgodnie z dokumentacją on-line nie jest jeszcze dostępna: "Wybierz, aby drukować tylko znaki na pozycjach wymienionych na liście znaków. To samo co -b na razie, ale internacjonalizacja to zmieni." [ gnu.org/software/coreutils/manual/html_node/…
LEo

@LEo Na podstawie linku w Twoim drugim komentarzu wydaje się, że używasz systemu operacyjnego opartego na GNU, prawdopodobnie GNU / Linux, więc w takim przypadku jest to oczekiwane - wspominam o tym na końcu mojej odpowiedzi. To działało wtedy (i działa teraz) dla mnie na FreeBSD (i prawdopodobnie w niektórych innych systemach operacyjnych), ale nie działało (i jeszcze nie działa) na GNU / Linuksie, w tym przypadku wspomniałem na końcu o alternatywnych metodach. Osobiście nie mogę się doczekać, aż ktoś znajdzie i zaoferuje wolny czas na internacjonalizację niezbędną do tego, aby zestaw narzędzi GNU działał tak dobrze, jak inni w tym zakresie.
rowanthorpe

0

Oto prosty skrypt, który kończy się przy użyciu ddwspomnianego tutaj podejścia:

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.