2016-01-18 2 views
2

В качестве упражнения я поставил себе задачу рекурсивно перечислять файлы, используя встроенные bash. Я особенно не хочу использовать ls или find, и я бы предпочел не использовать setopt extendedglob. Следующее работает, но я не вижу, как его расширить с помощью /.* для отображения скрытых файлов. Есть ли простой способ?Рекурсивно перечислять скрытые файлы без ls, find или extendedglob

g() { for k in "$1"/*; do # loop through directory 
[[ -f "$k" ]] && { echo "$k"; continue; }; # echo file path 
[[ -d "$k" ]] && { [[ -L "$k" ]] && { echo "$k"; continue; }; # echo symlinks but don't follow 
g "$k"; }; # start over with new directory 
done; }; g "/Users/neville/Desktop" # original directory 

Добавлено позже: извините - я должен был сказать: 'Баш-3,2 на OS X'

+1

Начиная с 'bash' 4.1, вы можете использовать расширенные шаблоны в аргументе' == 'и'! = 'Без явного включения' extglob', если это ваше возражение. – chepner

ответ

2

Изменить

for k in "$1"/*; do 

в

for k in "$1"/* "$1"/.[^.]* "$1"/..?*; do 

Второй Глоб соответствует всем файлы, имена которых начинаются с точки, за которой следует точка, отличная от точки, а третья соответствует всем файлам, имена которых начинаются с двух точек за которым следует что-то. Между двумя из них они будут соответствовать всем скрытым файлам, отличным от записей . и ...

К сожалению, если не установлен параметр оболочки nullglob, те (как первый глобус) могут оставаться как есть, если нет файлов, имена которых соответствуют (что весьма вероятно в случае третьего), поэтому необходимо убедитесь, что имя фактически является файлом.

Альтернативой было бы использовать гораздо более простой глобус "$1"/.*, который всегда будет соответствовать записям каталога . и .. и поэтому всегда будет заменен. В этом случае необходимо удалить две записи из списка:

for k in "$1"/* "$1"/.*; do 
    if ! [[ $k =~ /\.\.?$ ]]; then 
    # ... 
    fi 
done 

(.. Это все еще возможно "$1"/* оставаться в списке, хотя так, что не помогает столько, сколько он может)

+0

Мне нравится этот ответ лучше, чем у меня (и именно так я лично это сделал), потому что он работает в оболочках, отличных от Bash. Но поскольку OP действительно сказал «bash» конкретно, я пошел вперед и внедрил решение GLOBIGNORE, которое автоматически пропускает '.' и' ..'. :) – dannysauer

+0

Оба эти работы хорошо, и я вижу, что они делают, но я не понимаю, почему. Без них использование «$ 1» /.* медленно создает много каталогов:/Users/neville/Desktop/test dir /./././././././././ ./. - Почему это? Является ли файл-точка действующим как символическая ссылка на каталог, и если да, то почему она не остановлена ​​моей ловушкой символики? Если бы я был счастлив перечислить все точечные и двухточечные файлы UNIX, как это изменить? –

0

можно указать несколько аргументов в for:

for k in "$1"/* "$1"/.*; do 

Но если искать .* в каталогах, вы должны знать, что это также дает вам . и .. файлы. Вам также может быть предоставлен несуществующий файл, если совпадение "$1"/* совпадает, поэтому я тоже это проверил.

Имея это в виду, это то, как я бы исправить цикл:

g() { 
    local k subdir 
    for k in "$1"/* "$1"/.*; do # loop through directory 
     [[ -e "$k" ]] || continue # Skip missing files (unmatched globs) 
     subdir=${k##*/} 
     [[ "$subdir" = . ]] || [[ "$subdir" = .. ]] && continue # Skip the pseudo-directories "." and ".." 

     if [[ -f "$k" ]] || [[ -L "$k" ]]; then 
      printf %s\\n "$k" # Echo the paths of files and symlinks 
     elif [[ -d "$k" ]]; then 
      g "$k" # start over with new directory 
     fi 
    done 
} 
g ~neville/Desktop 

Здесь обалденный вид ${k##*/} это просто быстрый способ взять базовое имя файла, в то время как local был помещен в так что переменные не изменяют никаких существующих переменных в оболочке. более

Одна вещь, которую я изменил это echo "$k" к printf %s\\n "$k", потому что echo это непоправимо испорчено в своем обращении аргумента и следует избегать с целью повторив неизвестной переменной. (См. Rich's sh tricks для объяснения того, как он сводится к -n и -e бросает в работу гаечный ключ.)

Кстати, это НЕ будет печатать сокеты или фикс - это намеренно?

1

Установите файл GLOBIGNORE для исключения . и .., который неявно включает «shopt -u dotglob». Тогда ваш исходный код работает без каких-либо изменений.

[email protected] [/home/user/dir] 
$ touch file 
[email protected] [/home/user/dir] 
$ touch .dotfile 
[email protected] [/home/user/dir] 
$ echo * 
file 
[email protected] [/home/user/dir] 
$ GLOBIGNORE=".:.." 
[email protected] [/home/user/dir] 
$ echo * 
.dotfile file 

Обратите внимание, что это зависит от типа bash. В частности, он не работает в ksh.

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