Chciałbym wydrukować liczbę znaków w każdym wierszu pliku tekstowego za pomocą polecenia unix. Wiem, że z PowerShell jest to proste
gc abc.txt | % {$_.length}
ale potrzebuję polecenia unix.
Odpowiedzi:
Użyj Awk.
awk '{ print length }' abc.txt
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.
IFS=
w read
poleceniu, gdy chcesz wczytać dowolne dane. A więc IFS= read -r
. read
używa IFS
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 linia foo 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
Oto przykład użycia xargs
:
$ xargs -d '\n' -I% sh -c 'echo % | wc -c' < file
Spróbuj tego:
while read line
do
echo -e |wc -m
done <abc.txt
echo -e | wc -m
, prawda? To bezużyteczne użycie poleceń; powłoka może liczyć znaki w zmiennej. Plus echo -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.