Чому `ls -l` налічує більше файлів, ніж я?


25

Мабуть, я не можу порахувати. Я думаю, що в ньому є три файли/media

$ tree /media
/media
├── foo
├── onex
└── zanna
3 directories, 0 files

Однак ls -lзнахідки 12.

$ ls -l /media
total 12
drwxr-xr-x  2 root root 4096 Jul 31 20:57 foo
drwxrwxr-x  2 root root 4096 Jun 26 06:36 onex
drwxr-x---+ 2 root root 4096 Aug  7 21:17 zanna

І, якщо я це роблю, ls -laя отримую лише .і ..на додаток до вищезазначеного, але підрахунок єtotal 20

Яке пояснення?

Відповіді:


33

12Ви бачите, не кількість файлів, але кількість дискових блоків споживається.

Від info coreutils 'ls invocation':

 For each directory that is listed, preface the files with a line
 `total BLOCKS', where BLOCKS is the total disk allocation for all
 files in that directory.  The block size currently defaults to 1024
 bytes, but this can be overridden (*note Block size::).  The
 BLOCKS computed counts each hard link separately; this is arguably
 a deficiency.

Загальна сума переходить від 12того, 20коли ви використовуєте ls -laзамість того, ls -lщо ви рахуєте два додаткові каталоги: .і ... Ви використовуєте чотири дискові блоки для кожного (порожнього) каталогу, тож ваш загальний обсяг перебуває від 3 × 4 до 5 × 4. (Ймовірно, ви використовуєте один блок дисків у розмірі 4096 байт для кожного каталогу; як infoпоказує сторінка, утиліта не перевіряє формат диска, але передбачає розмір блоку, 1024якщо не вказано інше.)

Якщо ви хочете просто отримати кількість файлів, ви можете спробувати щось на кшталт

ls | wc -l

13
ls | wc -lне вдасться, якщо у назві файлу є файли з новим рядком. Це більш стійкий:find . -mindepth 1 -maxdepth 1 -printf . | wc -c
Flimm

20
"якщо в іменах файлів є новий рядок" ... здригається
Petah

8
Як man lsвам скажу, ви можете уникнути контрольних символів за допомогою -b(уникає їх) або -q(опускає їх). Тож для підрахунку, ls -1q | wc -lце безпечно та точно для показу не прихованих файлів. ls -1qA | wc -lрахувати приховані файли (але не .і ..). Я використовую -1замість того, -lщо це повинно бути швидшим.
Олі

18

user4556274 вже відповів на чому . Моя відповідь служить лише для надання додаткової інформації про те, як правильно рахувати файли.

У спільноті Unix загальна думка полягає в тому, що розбір результатів ls- дуже погана ідея , оскільки назви файлів можуть містити контрольні або приховані символи. Наприклад, через символ нового рядка в імені файлу у нас єls | wc -l сказали нам, що у виході ls(який він є) є 5 рядків , але насправді у каталозі є лише 4 файли.

$> touch  FILE$'\n'NAME                                                       
$> ls                                                                         
file1.txt  file2.txt  file3.txt  FILE?NAME
$> ls | wc -l
5

Спосіб №1: знайти корисність

findКоманда, яка , як правило , використовується для роботи навколо розбору імен файлів, може допомогти нам тут надрукувавши номер індексного дескриптора . Будь то каталог або файл, він має лише один унікальний номер inode. Таким чином, використовуючи -printf "%i\n"та виключаючи .через, -not -name "."ми можемо мати точний підрахунок файлів. (Зверніть увагу на використання -maxdepth 1для запобігання рекурсивного спуску в підкаталоги)

$> find  -maxdepth 1 -not -name "." -print                                    
./file2.txt
./file1.txt
./FILE?NAME
./file3.txt
$> find  -maxdepth 1 -not -name "." -printf "%i\n" | wc -l                    
4

Спосіб №2: глоблстар

Простий, швидкий і переважно портативний спосіб:

$ set -- * 
$ echo $#
228

setкоманда використовується для встановлення позиційних параметрів оболонки ( $<INTEGER>змінних, як у echo $1). Це часто використовується для обходу /bin/shобмеження відсутніх масивів. Версію, яка виконує додаткові перевірки, можна знайти в Gille на Unix & Linux.

У оболонках, які підтримують масиви, наприклад bash, ми можемо використовувати

items=( dir/* )
echo ${#items[@]}

як запропонував у коментарях steeldriver .

Аналогічний трюк findметоду, який використовувався wcта globstar, можна використовувати statдля підрахунку чисел у рядках у рядку:

$> LC_ALL=C stat ./* --printf "%i\n" | wc -l                                          
4

Альтернативний підхід - використовувати підстановку в forциклі. (Зауважте, цей тест використовує інший каталог, щоб перевірити, чи не підходить цей підхід до підкаталогів, а це не - 16 - це перевірена кількість елементів у моєму ~/bin)

$> count=0; for item in ~/bin/* ; do count=$(($count+1)) ; echo $count ; done | tail -n 1                                
16

Метод №3: інші мови / перекладачі

Python також може вирішувати проблемні назви файлів, друкуючи довжину списку, заданого моєю os.listdir()функцією (яка не є рекурсивною, і буде лише перелічувати елементи в каталозі, наведені як аргумент).

$> python -c "import os ; print os.listdir('.')"                              
['file2.txt', 'file1.txt', 'FILE\nNAME', 'file3.txt']
$>  python -c "import os ; print(len(os.listdir('.')))"                    
4

Дивись також


2
В bash, іншим варіантом було б використання масиву, наприклад items=( dir/* ); echo ${#items[@]}(додавання shopt -s dotglobдля включення прихованих файлів).
steeldriver

1
Друк номерів введення полегшує фільтрацію твердих посилань за бажанням за допомогою find | sort -u | wc -l.
Пітер Кордес

@steeldriver: Я думаю, що метод bash-array навряд чи буде швидшим. Якщо ви хочете, щоб він був рекурсивним, вам потрібно використовувати items=( dir/** )shopt -s globstar), але bash не користується додатковими метаданими з readdir, тому він реєструє кожну запис каталогу, щоб побачити, чи це сам каталог. Багато файлових систем зберігають файли у записі каталогів, тому readdir може повернути його, не маючи доступу до входів. (напр., останній XFS, який не використовується за замовчуванням, має це, і я думаю, що у ext4 його було довше.) Якщо ви straceзнайдете, ви побачите набагато менше statсистемних викликів, ніж розшарування bash.
Пітер Кордес

2
Чому б просто не використовувати print(len(os.listdir('.')))? Набирає менше символів, а також уникає доступу до атрибутів, що підкреслюються подвійно.
edwinksl

1
@edwinksl відредаговано, thx
Сергій Колодяжний
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.