Jest to o kilka rzędów wielkości szybsze niż zastosowanie wc -c do każdej linii!
aerijman
@aerijman w przypadku tego typu problemów liczba utworzonych procesów jest zazwyczaj tym, co powoduje największą różnicę w wydajności.
MarcH
Jeśli wiersz w pliku zawiera emotikony, nie da to oczekiwanej długości.
user5507535
@ user5507535, to zależy od tego, jakiej „długości” faktycznie oczekujesz. Istnieje wiele możliwych definicji Unicode (mawk używa bajtów, nie sprawdza gawk).
Jan Hudec
16
while IFS= read -r line; doecho${#line}; done < abc.txt
Jest to POSIX, więc powinno działać wszędzie.
Edycja: dodano -r zgodnie z sugestią Williama.
Edycja: uważaj na obsługę Unicode. Bash i zsh, z prawidłowo ustawionymi ustawieniami lokalnymi, pokażą liczbę punktów kodowych, ale myślnik pokaże bajty - więc musisz sprawdzić, co robi twoja powłoka. I tak istnieje wiele innych możliwych definicji długości w Unicode, więc zależy to od tego, czego naprawdę chcesz.
Edycja: przedrostek z, IFS=aby uniknąć utraty początkowych i końcowych spacji.
+1, ale ... to się nie powiedzie, jeśli dane wejściowe zawierają „\”. Użyj read -r
William Pursell
Jeśli wiersz w pliku zawiera emotikony, nie da to oczekiwanej długości.
user5507535
@ user5507535, właściwie to zależy od oczekiwanej „długości”. Istnieje wiele możliwych definicji Unicode (ale w tym przypadku różne powłoki będą faktycznie działać inaczej).
Jan Hudec
Zawsze ustawiaj IFS=w readpoleceniu, gdy chcesz wczytać dowolne dane. A więc IFS= read -r. readużywa IFSdo dzielenia słów i mimo że wszystkie podzielone słowa są następnie wklejane z powrotem do jednej dostępnej zmiennej ( line), nie ma gwarancji, że zostaną wklejone z powrotem razem ze wszystkimi oryginalnymi znakami separatora, które posiadały lub tylko jednym potencjalnie innym jedynki. Na przykład przy domyślnym IFS linia foo barmoże stać się foo bartracąc 7 spacji. (Tak jak w przypadku, gdy przepełnienie stosu stracił sąsiednie spacje w tym przykładowym ciągu w tym komentarzu).
mtraceur
@mtraceur, dokumentacja wyraźnie mówi, że „pozostałe słowa i ich separatory są przypisane do nazwiska”, więc są one wklejane z powrotem razem z oryginalnym separatorem. To jednak nie dotyczy wiodących i końcowych ograniczników, które są rzeczywiście utracone. Więc masz rację, IFSpowinieneś być ustawiony, ale problem, gdy tak nie jest, jest bardziej subtelny.
Jan Hudec
4
Wypróbowałem inne odpowiedzi wymienione powyżej, ale są one bardzo dalekie od przyzwoitych rozwiązań w przypadku dużych plików - zwłaszcza gdy rozmiar pojedynczej linii zajmuje więcej niż ~ 1/4 dostępnej pamięci RAM.
Zarówno bash, jak i awk siorbią całą linię, mimo że w przypadku tego problemu nie jest to potrzebne. Bash wyświetli błąd, gdy linia będzie zbyt długa, nawet jeśli masz wystarczająco dużo pamięci.
Zaimplementowałem niezwykle prosty, dość niezoptymalizowany skrypt Pythona, który podczas testowania z dużymi plikami (~ 4 GB na wiersz) nie sypie i jest zdecydowanie lepszym rozwiązaniem niż podane.
Jeśli jest to kod krytyczny czasowo dla produkcji, możesz przepisać pomysły w C lub przeprowadzić lepszą optymalizację przy wywołaniu odczytu (zamiast czytać tylko jeden bajt na raz), po przetestowaniu, że jest to rzeczywiście wąskie gardło.
Kod zakłada, że znak nowej linii to znak wysuwu wiersza, co jest dobrym założeniem dla Uniksa, ale YMMV w systemie Mac OS / Windows. Upewnij się, że plik kończy się znakiem nowej linii, aby zapewnić, że liczba znaków w ostatnim wierszu nie zostanie przeoczona.
from sys import stdin, exit
counter = 0
while True:
byte = stdin.buffer.read(1)
counter += 1
if not byte:
exit()
if byte == b'\x0a':
print(counter-1)
counter = 0
To „echo%” nie obsługuje niebezpiecznych znaków, które wymagają cytowania z powłoki. Dodatkowo "xargs" będzie dzielił twój plik na spacje i znaki nowej linii, a nie tylko znaki nowej linii, jak żądał oryginalny plakat.
Miałeś na myśli echo -e | wc -m, prawda? To bezużyteczne użycie poleceń; powłoka może liczyć znaki w zmiennej. Plus echo -ejest całkowicie niekompatybilny i działa w połowie powłok, podczas gdy rozpoczęcie od jednej sekwencji ucieczki działa w innej, a nic w pozostałych.
while IFS= read -r line; do echo ${#line}; done < abc.txt
Jest to POSIX, więc powinno działać wszędzie.
Edycja: dodano -r zgodnie z sugestią Williama.
Edycja: uważaj na obsługę Unicode. Bash i zsh, z prawidłowo ustawionymi ustawieniami lokalnymi, pokażą liczbę punktów kodowych, ale myślnik pokaże bajty - więc musisz sprawdzić, co robi twoja powłoka. I tak istnieje wiele innych możliwych definicji długości w Unicode, więc zależy to od tego, czego naprawdę chcesz.
Edycja: przedrostek z,
IFS=
aby uniknąć utraty początkowych i końcowych spacji.źródło
IFS=
wread
poleceniu, gdy chcesz wczytać dowolne dane. A więcIFS= read -r
.read
używaIFS
do dzielenia słów i mimo że wszystkie podzielone słowa są następnie wklejane z powrotem do jednej dostępnej zmiennej (line
), nie ma gwarancji, że zostaną wklejone z powrotem razem ze wszystkimi oryginalnymi znakami separatora, które posiadały lub tylko jednym potencjalnie innym jedynki. Na przykład przy domyślnym IFS liniafoo bar
może stać sięfoo bar
tracąc 7 spacji. (Tak jak w przypadku, gdy przepełnienie stosu stracił sąsiednie spacje w tym przykładowym ciągu w tym komentarzu).IFS
powinieneś być ustawiony, ale problem, gdy tak nie jest, jest bardziej subtelny.Wypróbowałem inne odpowiedzi wymienione powyżej, ale są one bardzo dalekie od przyzwoitych rozwiązań w przypadku dużych plików - zwłaszcza gdy rozmiar pojedynczej linii zajmuje więcej niż ~ 1/4 dostępnej pamięci RAM.
Zarówno bash, jak i awk siorbią całą linię, mimo że w przypadku tego problemu nie jest to potrzebne. Bash wyświetli błąd, gdy linia będzie zbyt długa, nawet jeśli masz wystarczająco dużo pamięci.
Zaimplementowałem niezwykle prosty, dość niezoptymalizowany skrypt Pythona, który podczas testowania z dużymi plikami (~ 4 GB na wiersz) nie sypie i jest zdecydowanie lepszym rozwiązaniem niż podane.
Jeśli jest to kod krytyczny czasowo dla produkcji, możesz przepisać pomysły w C lub przeprowadzić lepszą optymalizację przy wywołaniu odczytu (zamiast czytać tylko jeden bajt na raz), po przetestowaniu, że jest to rzeczywiście wąskie gardło.
Kod zakłada, że znak nowej linii to znak wysuwu wiersza, co jest dobrym założeniem dla Uniksa, ale YMMV w systemie Mac OS / Windows. Upewnij się, że plik kończy się znakiem nowej linii, aby zapewnić, że liczba znaków w ostatnim wierszu nie zostanie przeoczona.
from sys import stdin, exit counter = 0 while True: byte = stdin.buffer.read(1) counter += 1 if not byte: exit() if byte == b'\x0a': print(counter-1) counter = 0
źródło
Oto przykład użycia
xargs
:$ xargs -d '\n' -I% sh -c 'echo % | wc -c' < file
źródło
Spróbuj tego:
while read line do echo -e |wc -m done <abc.txt
źródło
echo -e | wc -m
, prawda? To bezużyteczne użycie poleceń; powłoka może liczyć znaki w zmiennej. Plusecho -e
jest całkowicie niekompatybilny i działa w połowie powłok, podczas gdy rozpoczęcie od jednej sekwencji ucieczki działa w innej, a nic w pozostałych.