Як надрукувати кількість символів у кожному рядку текстового файлу


83

Я хотів би надрукувати кількість символів у кожному рядку текстового файлу за допомогою команди unix. Я знаю, що з PowerShell це просто

gc abc.txt | % {$_.length}

але мені потрібна команда unix.

Відповіді:


156

Використовуйте Awk.

awk '{ print length }' abc.txt

2
Це на кілька порядків швидше, ніж застосування wc -c до кожного рядка!
aerijman

Для цього типу проблем @aerijman, як правило, найбільше відрізняється кількість процесів.
MarcH

Якщо рядок у файлі містить смайли, це не дасть очікуваної довжини.
user5507535

@ user5507535, це залежить від того, яку “довжину” ви насправді очікуєте. Існує багато можливих визначень для Unicode (mawk використовує байти, не перевіряв gawk).
Ян

17
while IFS= read -r line; do echo ${#line}; done < abc.txt

Це POSIX, тому він повинен працювати скрізь.

Редагувати: Додано -r, як запропонував Вільям.

Редагувати: Остерігайтеся обробки Unicode. Bash і zsh, з правильно встановленою мовою, відображатимуть кількість кодових точок, але тире - байти - тому вам доведеться перевірити, що робить ваша оболонка. І тоді в Unicode є багато інших можливих визначень довжини, тож це залежить від того, що ви насправді хочете.

Редагувати: префікс з, IFS=щоб уникнути втрати пробілів на початку та в кінці.


+1, але ... це не вдасться, якщо введення містить '\'. Використовуйте read -r
William Pursell

Якщо рядок у файлі містить смайли, це не дасть очікуваної довжини.
user5507535

@ user5507535, насправді, це залежить від того, яку “довжину” ви очікуєте. Існує багато можливих визначень для Unicode (але в цьому випадку різні оболонки насправді по-різному роблять).
Ян

Завжди встановлюйте IFS=на readкоманду , коли потрібно , щоб читати в довільних даних. Отже IFS= read -r. readвикористовує IFSдля розбиття слів, і хоча всі розбиті слова потім вставляються назад в одну доступну змінну ( line), немає гарантії, що вони будуть вставлені назад разом із усіма оригінальними символами-роздільниками, які вони мали, або лише одним потенційно різним ті. Наприклад, за замовчуванням IFS, рядок foo barможе стати foo bar, втративши 7 пробілів. (На зразок того, як у цьому коментарі Stack Overflow втратив сусідні пробіли в цьому прикладі рядка).
mtraceur

@mtraceur, в документації прямо сказано, що "решта слів та їх проміжні роздільники присвоюються прізвищу", тому вони дійсно вставляються назад разом із оригінальним роздільником. Однак це не піклується про провідні та кінцеві роздільники, які справді втрачені. Отже, ви маєте рацію, це IFSслід встановити, але проблема, коли це не так, є більш тонкою.
Ян

4

Я спробував інші відповіді, перераховані вище, але вони дуже далекі від гідних рішень при роботі з великими файлами - особливо, коли розмір одного рядка займає більше ~ 1/4 доступної оперативної пам'яті.

І bash, і awk скрупують весь рядок, хоча для цієї проблеми це не потрібно. Bash помилиться, якщо рядок буде занадто довгим, навіть якщо у вас достатньо пам'яті.

Я реалізував надзвичайно простий, досить неоптимізований скрипт python, який при тестуванні з великими файлами (~ 4 ГБ на рядок) не викликає непорозумінь і є набагато кращим рішенням, ніж подані.

Якщо це критично важливий для часу код для виробництва, ви можете переписати ідеї на C або виконати кращі оптимізації прочитаного виклику (замість того, щоб читати лише один байт за раз), протестувавши, що це справді вузьке місце.

Код передбачає, що новий рядок є символом подачі лінії, що є гарним припущенням для Unix, але YMMV для Mac OS / Windows. Переконайтеся, що файл закінчується подачею рядків, щоб переконатися, що кількість символів останнього рядка не пропущена.

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

1
Питання було щодо "текстового" файлу. Я не думаю, що 4 Гб на рядок відповідає розумному визначенню текстового файлу.
MarcH

3

Ось приклад використання xargs:

$ xargs -d '\n' -I% sh -c 'echo % | wc -c' < file

Цей "echo%" не обробляє небезпечні символи, які потребують цитування з оболонки. Крім того, "xargs" буде розділяти ваш файл на пробіли та нові рядки, а не лише на нові рядки, як вимагав оригінальний плакат.
велика рогата худоба

1

Спробуйте це:

while read line    
do    
    echo -e |wc -m      
done <abc.txt    

Ви мали на увазі echo -e | wc -m, чи не так? Це марне використання команд; оболонка може рахувати символи у змінній. Плюс echo -eабсолютно несумісний і працює в половині снарядів, а починаючи з якоїсь послідовності втечі працює в деяких інших, а в решті нічого.
Jan Hudec
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.