uzyskać pierwsze X znaków z polecenia kota?


42

Mam plik tekstowy, który wypisuję do zmiennej w moim skrypcie powłoki. Potrzebuję jednak tylko pierwszych 50 znaków.

Próbowałem użyć, cat ${filename} cut -c1-50ale otrzymuję znacznie więcej niż pierwszych 50 znaków? Może to wynikać z cutszukania linii (nie w 100% pewności), podczas gdy ten plik tekstowy może być jednym długim ciągiem - to naprawdę zależy.

Czy istnieje narzędzie, do którego mogę wpakować potok, aby uzyskać pierwsze X znaków z catpolecenia?


10
Zapomniałeś |? cat ${filename} | cut -c1-50
DisplayName

@DisplayName naprawiono, dziękuję za wyłapanie mojego błędu przepisywania.
jkj2000

1
@ jkj2000, wróciłem do starszej wersji, ponieważ było to pierwotne pytanie.
Ramesh

Odpowiedzi:


61
head -c 50 file

Zwraca pierwsze 50 bajtów.

Pamiętaj, że polecenie nie zawsze jest realizowane tak samo we wszystkich systemach operacyjnych. W systemach Linux i macOS zachowuje się w ten sposób. W systemie Solaris (11) musisz użyć wersji GNU w / usr / gnu / bin /


głowa nie ma -copcji. Pójdę za dd (1) zamiast.
mirabilos

7
Zauważ, że ta odpowiedź zakłada, że ​​plik zawiera tylko znaki ASCII, ponieważ OP poprosił o pierwsze X znaków, a nie bajty.
Calimo

2
@mirabilos To może nie być przenośne, ale moja wersja ( GNU coreutils 5.97) tak.
Yossarian

1
POSIX nie definiuje jednak -cjako prawidłowej opcji, więc jest zdecydowanie zależny od lokalnego środowiska. unix.com/man-page/posix/1/head
Jules

1
@Calimo Tak, wiem, ale próbowałem utworzyć plik tekstowy zawierający 100 znaków, a następnie uruchomić polecenie i wypisać 50 znaków. Ale masz rację co do ASCII, ale skoro OP oznaczył to jako odpowiedź, w jego przypadku nie było żadnych.
DisplayName

27

Twoje cutpolecenie działa, jeśli do przesyłania danych używasz potoku:

cat ${file} | cut -c1-50 

Lub unikając bezużytecznego korzystania z kota i czyniąc go nieco bezpieczniejszym:

cut -c1-50 < "$file"

Zauważ, że powyższe polecenia wypiszą pierwsze 50 znaków (lub bajtów, w zależności od cutimplementacji) każdej linii wejściowej . Powinien zrobić to, czego oczekujesz, jeśli, jak mówisz, plik ma jedną wielką linię.


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

Zwraca pierwsze 50 bajtów.


dd nie ma status=noneflagi. 2>/dev/nullZamiast tego używaj (i odpowiednio cytuj): dd if="$filename" bs=1 count=50 2>/dev/null(mimo to rozważ użycie w bs=50 count=1celu zmniejszenia liczby zaangażowanych wywołań systemowych).
mirabilos

1
@mirabilos dd ma to, status=nonegdy używasz Ubuntu 14.04, coreutils 8.21, ale masz rację, 2>/dev/nulljeśli używasz wcześniejszej wersji.
doneal24

1
@mirabilos Większość dystrybucji Linuksa używa GNU coreutils, podobnie jak FreeBSD i inne BSD. Jest dostępny w systemie Solaris jako pakiet gnu-coreutils. Tak, jest to „Unix i Linux” i zarówno systemy Unix, jak i Linux używają rdzeni GNU.
doneal24,

2
Nie, systemy Unix zazwyczaj nie używają narzędzi GNU. GNU to nawet skrót od „GNU nie jest Uniksem”. Proszę trzymać się rozwiązań przenośnych lub, jeśli musisz podać rozwiązania tylko GNU, określ to i, jeśli to możliwe, pokaż równoważne rozwiązanie przenośne.
mirabilos

1
Ściśle mówiąc, to robi jeden read()z 50 bajtów. Jeśli na przykład filejest potokiem i dostępnych jest mniej znaków, zwracanych jest mniej bajtów. Aby mieć ekwiwalent head -c50, musisz użyć specyficznego dla GNU iflag=fullblock.
Stéphane Chazelas

4

Większość odpowiedzi do tej pory zakłada, że ​​1 bajt = 1 znak, co może nie mieć miejsca, jeśli używasz ustawień regionalnych innych niż ASCII.

Nieco bardziej niezawodny sposób:

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

Zauważ, że zakłada to:

  1. Używasz ksh93, bash(lub ostatni zshlub mksh(choć tylko wielo-bajtowego kodowania obsługiwanego przez mkshUTF-8, a dopiero później set -o utf8-mode)) i wersja head, która wspiera -c(najbardziej zrobić w dzisiejszych czasach, ale nie ściśle standard).
  2. Bieżące ustawienia narodowe są ustawione na to samo kodowanie co plik (wpisz locale charmapi file -- "$filename"sprawdź to); jeśli nie, ustaw to za pomocą np. LC_ALL=en_US.UTF-8)
  3. Wziąłem pierwsze 200 bajtów pliku head, przyjmując najgorszy przypadek UTF-8, w którym wszystkie znaki są zakodowane maksymalnie na 4 bajtach. Powinno to obejmować większość przypadków, o których mogę myśleć.

Oczywiście zakłada to także GNU headlub inną jego implementację, która dodaje -copcję nōn-standard . Ale już potrzebujesz GNU bash. (Uwaga: mkshtryb UTF-8 mógłby to zrobić dla plików zakodowanych w UTF-8.) Zapytałbym OP, czy wymagają oktetów lub znaków wielobajtowych, po prostu „znaki” to termin niejasny / gerneryczny.
mirabilos

To również zakłada $filenamelub $testStringnie zawiera pustej nowej linii lub symboli wieloznacznych lub zaczyna się od -.
Stéphane Chazelas

${var:offset:length}Konstrukt używasz tutaj rzeczywiście pochodzi ksh93i jest również wspierany przez najnowsze wersje zsh( zshposiada własne $testString[1,50]). Musisz ${testString:0:50} się ksh93a zshjednak.
Stéphane Chazelas

Właśnie zredagowałem moją odpowiedź, aby odpowiedzieć na powyższe komentarze
Calimo,

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

Inny wariant (dla pierwszego wiersza w pliku)

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

Jest to nadużycie narzędzi wysokiego poziomu - i skłonność do nie robienia tego, co chcesz, np. Jeśli są one zależne od ustawień regionalnych.
mirabilos

@mirabilos Co masz na myśli mówiąc o narzędziach wysokiego poziomu : readi echo? Czy bash expansion?
Costas

grep(regexp) i tak, użycie powłoki tutaj (wskazówka: pierwsza linia może być duża). (To powiedziawszy, bashizmu nie ma również w POSIX, ale większość pocisków to implementuje.)
mirabilos

0

1. W przypadku plików ASCII, jak lub @DisplayName mówi:

head -c 50 file.txt

wypisze na przykład pierwsze 50 znaków pliku file.txt.

2. W przypadku danych binarnych użyj hexdumpdo wydrukowania ich jako znaków szesnastkowych:

hexdump -n 50 -v file.bin

wypisze na przykład pierwsze 50 bajtów pliku.bin.

Zauważ, że bez -vopcji pełnej hexdumpzastąpiłoby powtarzające się linie gwiazdką ( *). Zobacz tutaj: https://superuser.com/questions/494245/what-does-an-asterisk-mean-in-hexdump-output/494613#494613 .


-2

Możesz do tego użyć sed, który dość łatwo poradzi sobie z tym problemem

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

Ciekawe, jak to się potępiło, jeśli rozwiązuje pytanie OP: „Potrzebuję tylko pierwszych 50 znaków” To osiąga to, o co poproszono bez UUOC (Bezużyteczne użycie kota)
munkeyoto

1
Ta odpowiedź podaje pierwsze pięćdziesiąt znaków każdego wiersza w pliku, a nie tylko pierwsze 50 pliku. Nie drukuje też niczego, jeśli wszystkie linie mają mniej niż 50 znaków. Twoje rozwiązanie działałoby lepiej zsed -n -e '1s/^\(.\{50\}\).*/\1/p' ${filename}
doneal24

Zrozumiane może mieć po prostu: head -n 1 | sed -e 's / ^ (. \ {50 \}). * / \ 1 /' ... I rozwiązałoby to problem. OP stwierdził: „potrzebuje tylko pierwszych 50 znaków”
munkeyoto

1
Nie. Jeśli pierwszy wiersz ma tylko 49 znaków, nic nie wyświetli.
doneal24,

Doug Zrozumiałem to po raz pierwszy, ale OP nie wspomniało o drukowaniu, jeśli linia zawierała mniej niż 50 znaków, więc nadal nie rozumiem twojego sensu, ani też sensu tego odrzucenia, ponieważ znów wpadł w to, z czym pracowałby głowa: głowa -n 1 $ {nazwa pliku} | sed -n -e '1s / ^ (. \ {50 \}). * / \ 1 / p'
munkeyoto
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.