2016-08-13 7 views
1

Я хочу команду bash, которая вернет таблицу, где каждая строка является человекочитаемым размером файла, количеством строк и именем файла. Таблица должна сортироваться по размеру файла.Человекочитаемый размер файла и количество строк

Я пытался это сделать, используя комбинацию du -hs, wc -l и sort -h и find.

Вот где я нахожусь:

find . -exec echo $(du -h {}) $(wc -l {}) \; | sort -h 
+0

показать нам, что у вас есть – eckes

+0

@eckes, отредактированный с помощью нерабочего кода – Hatshepsut

ответ

1

Ваш подход не оправдал не только потому, что оболочки расширила свою команды замены ($(...)) фронта, но более фундаментально, потому что вы не можете пройти оболочку командной строки до find

find «S -exec действие может вызывать только внешние утилиты с буквальных аргументов - единственное небуквальное аргумент Поддерживается ли {}, представляющее имя файла (ов) в руке.

choroba's answer исправляет немедленную проблему, вызывающую отдельный экземпляр оболочки в каждой итерации, к которому команда оболочки для выполнения передается в качестве аргумента строки в (-exec bash -c '...' \;).
Хотя это работает (если вы передать значение {} в качестве аргумента вместо того, чтобы внедрить его в строке командной строки), это также довольно неэффективна, потому что несколько дочерних процессов создаются для каждого входа файл.

(В то время как это способ иметь find проход (обычно) все входные файлы на (обычно) одного вызова указанной внешней полезности, а именно - с терминатором +, а не \;, это не вариант здесь в связи с характером командной строки передается.)

эффективный и надежный [1] реализация, которая минимизирует количество дочерних процессов создано будет выглядеть следующим образом:

Примечание: Я предполагаю, что GNU утилиты здесь, благодаря использованию head -n -1 и sort -h.
Кроме того, я ограничивая выход find «s к файлов только (в отличие от каталогов), потому что wc -l работает только на файлов.

paste <(find . -type f -exec du -h {} +) <(find . -type f -exec wc -l {} + | head -n -1) | 
    awk -F'\t *' 'BEGIN{OFS="\t"} {sub(" .+$", "", $3); print $1,$2,$3}' | 
    sort -h -t$'\t' -k1,1 
  • Обратите внимание на использование -exec ... +, а не -exec ... \;, который гарантирует, что обычно все входные имена файлов передаются в одного вызова к внешней утилиты (если не все имена файлов помещаются на одной командной строки, вызовы эффективно распределяются, чтобы сделать как можно меньше вызовов).

  • wc -l {} + всегда выводит сводную строку, которая head -n -1 удаляет, но также выводит имена файлов после каждого подсчета строк.

  • paste объединяет линии от каждой команды (соответствующие входы которой предусмотрены подстановкой процесса <(...)) в один выходной поток.

  • Затем команда awk накладывает посторонние имена файлов, которые начинаются с wc с конца каждой строки.

  • Наконец, команда sort сортирует результат на 1-е (-k1,1) вкладка разделенных (-t$'\t') колонки человеческих считываемых числами (-h), такие как числа, которые du -h выходов (например, 1K).


[1] Как и с любой линии-ориентированной обработки, имена файлов со встроенными символами новой строки не поддерживаются, но я не считаю, что это в реальном мире проблема.

0

Проблема заключается в том, что ваша оболочка интерпретирует $(...), так find не получает их. Их экранирование не помогает (\$\(du -h {}\)), так как они становятся нормальными параметрами команд, а не заменой команды.

Для того, чтобы интерпретировать их как подстановки команд, чтобы вызвать новую оболочку, либо непосредственно

find . -exec bash -c 'echo $(du -h {}) $(wc -l {})' \; | sort -h 

или путем создания сценария и вызов его из find.

+0

Возможно ли только показать имя файла один раз? – Hatshepsut

+1

@Hatshepsut: Конечно, используйте 'wc -l <{}'. – choroba

+0

К сожалению, это сломается с именами файлов со встроенными метасимволами, такими как пробелы (и, возможно, со встроенными строками глобуса). Чтобы исправить это, вам придется использовать 'find. -exec bash -c 'echo "$ (du -h" $ 1 ") $ (wc -l <" $ 1 ")"' - {} \; | sort -h' Однако, хотя это исправляет непосредственную проблему OP и является заманчиво коротким, этот подход будет работать с плохо с большими наборами входных файлов, поскольку он создает несколько дочерних процессов _per filename_. – mklement0

1

Хорошо, я попробовал это с помощью find/-exec, но побег - это ад. С функцией оболочки он работает довольно прямо вперед:

#!/bin/bash 
function dir 
{ 
    du=$(du -sh "$1" | awk '{print $1}') 
    wc=$(wc -l < "$1") 
    printf "%10s %10s %s\n" $du $wc "${1#./}" 
} 

printf "%10s %10s %s\n" "size" "lines" "name" 
OIFS=$IFS; IFS="" 
find . -type f -print0 | while read -r -d $'\0' f; do dir "$f"; done 
IFS=$OIFS 

Использование basishm прочитать это даже своего рода безопасным при использовании NUL-терминатор. IFS необходим, чтобы избежать чтения, чтобы обрезать конечные пробелы в именах файлов.

BTW: $'\0' на самом деле не работает (так же, как '') - но это делает цель понятной.

выход

Примера:

 size  lines name 
     156K  708 sash 
     16K   64 hostname 
     120K  460 netstat 
     40K  110 fuser 
     644K  1555 dir/bash 
     28K   82 keyctl 
     2.3M  8067 vim 

 Смежные вопросы

  • Нет связанных вопросов^_^