Тест -d каталог true - подкаталог false (POSIX) - PullRequest
0 голосов
/ 25 апреля 2018

Я пытаюсь распечатать все каталоги / подкаталоги из заданного начального каталога.

for i in $(ls -A -R -p); do 
    if [ -d "$i" ]; then
            printf "%s/%s \n" "$PWD" "$i"
    fi
done; 

Этот скрипт возвращает все каталоги, найденные в. каталог и все файлы в этом каталоге, но по какой-то причине проверка не удалась для подкаталогов. Все каталоги заканчиваются на $ i, и результат выглядит точно так же.
Допустим, у меня есть следующая структура:

foo/bar/test

echo $ i print

foo/
bar/
test/

Пока содержимое папок перечислено так:

./foo:
file1
file2
./bar:
file1
file2

Тем не менее, оператор теста просто печатает:

PWD/TO/THIS/DIRECTORY/foo

По некоторым причинам он возвращает true для каталогов первого уровня, но false для всех подкаталогов.

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

Ответы [ 2 ]

0 голосов
/ 25 апреля 2018

Рассмотрим ваш вывод:

dir1:
dir1a

Теперь верно следующее:

[ -d dir1/dir1a ]

но это не то, что делает ваш код; вместо этого он запускается:

[ -d dir1a ]

Чтобы избежать этого, не пытайтесь анализировать ls; если вы хотите реализовать рекурсию в базовой версии POSIX sh, сделайте это самостоятельно:

callForEachEntry() {
  # because calling this without any command provided would try to execute all found files
  # as commands, checking for safe/correct invocation is essential.
  if [ "$#" -lt 2 ]; then
    echo "Usage: callForEachEntry starting-directory command-name [arg1 arg2...]" >&2
    echo "  ...calls command-name once for each file recursively found" >&2
    return 1
  fi
  # try to declare variables local, swallow/hide error messages if this fails; code is
  # defensively written to avoid breaking if recursing changes either, but may be faulty if
  # the command passed as an argument modifies "dir" or "entry" variables.
  local dir entry 2>/dev/null ||: "not strict POSIX, but available in dash"
  dir=$1; shift
  for entry in "$dir"/*; do
    # skip if the glob matched nothing
    [ -e "$entry" ] || [ -L "$entry" ] || continue
    # invoke user-provided callback for the entry we found
    "$@" "$entry"
    # recurse last for if on a baseline platform where the "local" above failed.
    if [ -d "$entry" ]; then
      callForEachEntry "$entry" "$@"
    fi
  done
}

# call printf '%s\n' for each file we recursively find; replace this with the code you
# actually want to call, wrapped in a function if appropriate.
callForEachEntry "$PWD" printf '%s\n'

find также может использоваться безопасно, но не в качестве замены для способа, которым ls использовался в исходном коде - for dir in $(find . -type d) точно так же глючит. Вместо этого см. Раздел «Сложные действия» и «Массовые действия» в Использование Find .

0 голосов
/ 25 апреля 2018

Как указано в комментариях, проблема заключается в том, что имена каталогов включают :, поэтому -d имеет значение false.

Я полагаю, что эта команда дает вам нужный вывод (хотя онатребуется Bash):

# enable globstar for ** 
# disabled in non-interactive shell (e.g. a script)
shopt -s globstar 

# print each path ending in a / (all directories)
# ** expands recursively
printf '%s\n' **/*/

Стандартным способом будет либо выполнить рекурсию самостоятельно, либо использовать find:

find . -type d
...