Как рекурсивно вывести список подкаталогов в Bash без использования команд "find" или "ls"? - PullRequest
11 голосов
/ 28 января 2010

Я знаю, что вы можете использовать команду find для этой простой работы, но я получил задание не использовать find или ls и выполнять эту работу. Как я могу это сделать?

Ответы [ 9 ]

30 голосов
/ 28 января 2010

вы можете сделать это только с помощью оболочки

#!/bin/bash
recurse() {
 for i in "$1"/*;do
    if [ -d "$i" ];then
        echo "dir: $i"
        recurse "$i"
    elif [ -f "$i" ]; then
        echo "file: $i"
    fi
 done
}

recurse /path

ИЛИ если у вас bash 4.0

#!/bin/bash
shopt -s globstar
for file in /path/**
do
    echo $file
done
11 голосов
/ 28 января 2010

Попробуйте использовать

tree -d
5 голосов
/ 07 марта 2010

Ниже приведена одна из возможных реализаций:

# my_ls -- recursively list given directory's contents and subdirectories
# $1=directory whose contents to list
# $2=indentation when listing
my_ls() {
  # save current directory then cd to "$1"
  pushd "$1" >/dev/null
  # for each non-hidden (i.e. not starting with .) file/directory...
  for file in * ; do
    # print file/direcotry name if it really exists...
    test -e "$file" && echo "$2$file"
    # if directory, go down and list directory contents too
    test -d "$file" && my_ls "$file" "$2  "
  done
  # restore directory
  popd >/dev/null
}

# recursively list files in current
#  directory and subdirectories
my_ls .

В качестве упражнения вы можете подумать о том, как изменить приведенный выше сценарий для печати полных путей к файлам (вместо просто с отступом имени файла / каталогов), возможно, избавившись от pushd / popd (и от необходимости второй параметр $2) в процессе.

Кстати, обратите внимание на использование test XYZ && command, которое полностью эквивалентно if test XYZ ; then command ; fi (т. Е. Выполнить command, если test XYZ успешно). Также отметим, что test XYZ эквивалентно [ XYZ ], то есть вышеупомянутое также эквивалентно if [ XYZ ] ; then command ; fi. Также обратите внимание, что любую точку с запятой ; можно заменить новой строкой, они эквивалентны.

Удалите условие test -e "$file" && (оставьте только echo) и посмотрите, что произойдет.

Удалите двойные кавычки вокруг "$file" и посмотрите, что произойдет, если каталог, содержимое которого вы видите, содержит имена файлов с пробелами в них. Добавьте set -x вверху скрипта (или вместо этого вызовите его как sh -x scriptname.sh), чтобы включить вывод отладки и посмотреть, что происходит подробно (чтобы перенаправить вывод отладки в файл, запустите sh -x scriptname.sh 2>debugoutput.txt).

Для просмотра списка скрытых файлов (например, .bashrc):

...
for file in * .?* ; do
  if [ "$file" != ".." ] ; then
    test -e ...
    test -d ...
  fi
done
...

Обратите внимание на использование != (сравнение строк) вместо -ne (числовое сравнение.)

Другая техника - порождение подпочв вместо использования pushd / popd:

my_ls() {
  # everything in between roundbrackets runs in a separatly spawned sub-shell
  (
    # change directory in sub-shell; does not affect parent shell's cwd
    cd "$1"
    for file in ...
      ...
    done
  )
}

Обратите внимание, что в некоторых реализациях оболочки существует жесткое ограничение (~ 4 КБ) на количество символов, которые могут быть переданы в качестве аргумента for (или любой встроенной или внешней команде в этом отношении.) Поскольку Оболочка расширяет встроенный * список всех совпадающих имен файлов перед тем, как фактически выполнить над ним for, вы можете столкнуться с проблемами, если * развернут внутри каталога с большим количеством файлов ( та же проблема, с которой вы столкнетесь при запуске, скажем, ls * в том же каталоге, например, получите сообщение об ошибке, например Command too long.)

4 голосов
/ 19 февраля 2016

Так как это для bash, удивительно, что это еще не было сказано:
(globstar действует с bash 4.0 +)

shopt -s globstar nullglob dotglob
echo **/*/

Вот и все.
Конечный слеш / предназначен для выбора только директорий.

Опция globstar активирует ** (поиск рекурсивно). Опция nullglob удаляет *, когда она не соответствует ни файлу / директории. Опция dotglob включает файлы, начинающиеся с точки (скрытые файлы).

1 голос
/ 28 января 2010

Как сказал Марк Байерс, вы можете использовать echo *, чтобы получить список всех файлов в текущем каталоге.

Команда / buildin test или [] имеет возможность проверить, является ли файл каталогом.

Примените рекурсию, и все готово.

1 голос
/ 28 января 2010

Команда du выводит список подкаталогов рекурсивно.

Я не уверен, что пустые каталоги упоминаются, хотя

0 голосов
/ 18 сентября 2018

На основании этого ответа ;используйте параметры оболочки для желаемого поведения сглаживания:

  • включите ** с globstar (Bash 4.0 или новее)
  • включите скрытые каталоги с dotglob
  • разверните пустую строку вместо **/*/, если нет совпадения с nullglob

, а затем используйте printf с форматированием %qдиректива заключать в кавычки имена каталогов со специальными символами в них:

shopt -s globstar dotglob nullglob
printf '%q\n' **/*/

, поэтому если у вас есть каталоги, такие как has space или даже содержащие новую строку, вы получите вывод, например

$ printf '%q\n' **/*/
$'has\nnewline/'
has\ space/

с одним каталогом на строку.

0 голосов
/ 07 марта 2010

Технически, ни find , ни ls не используются find2perl | perl или File :: Find непосредственно.

$ find2perl -type d | perl
$ perl -MFile::Find -e'find(sub{-d&&print"$File::Find::name\n"},".")'
0 голосов
/ 28 января 2010
$ function f { for i in $1/*; do if [ -d $i ]; then echo $i; f $i; fi; done }
$ mkdir -p 1/2/3 2/3 3
$ f .
./1
./1/2
./1/2/3
./2
./2/3
./3
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...