bash - проверить, существуют ли файлы и папки в операторе for - PullRequest
0 голосов
/ 25 января 2019

Я пытаюсь распечатать все файлы и папки каталога.Проблема в том, что распечатка из if (echo "$f out of if") в порядке, но код никогда не выполняет распечатки внутри операторов if.

Я уверен, что файлы и папки существуют (распечатано из if), и я проверил права доступа.

dirPath - это /tmp/testFolder.

for f in `ls $dirPath`; do
   echo "$f out of if"
   if [ -d $f ]; then
      echo "$f is a directory"
   fi
   if [ -f $f ]; then
      echo "$f is a file"
   fi
done

РЕДАКТИРОВАТЬ: Спасибо за ответ!Я пробовал это решение, но обнаружил проблему, заключающуюся в том, что «tmp / testFolder» (dirPath) тоже распознается как папка в цикле for (и мне нужно только найти подпапку).:

shopt -s globstar

for f in "$dirPath"/**; do
   echo "$f out of if"
   if [ -d $f ]; then
      echo "$f is a directory"
   fi
   if [ -f $f ]; then
      echo "$f is a file"
   fi
done

Ответы [ 5 ]

0 голосов
/ 25 января 2019

Исходя из ваших правок, вам нужно сравнивать, только если "$f" равно "$dataPath/", чтобы обнаружить и избежать печати каталога "$dataPath".(фактически используя !=) Продолжая ваш пример (и включив все комментарии о том, что вы не используете for i in ls что-нибудь` ...), вы можете сделать:

#!/bin/bash

shopt -s globstar   ## enable globstar

[ -z "$1" ] && {    ## validate at least 1 argument provided
    printf "error: insufficient input\nusage: %s path\n" "${0##*/}" >&2
    exit 1
}

datapath="$1"       ## set datapath

[ -d "$datapath" ] || { ## validate argument is a directory
    printf "error: invalid path - not a directory.\n" >&2
    exit 1;
}

## output absolute file/directory names
for f in "$datapath"/**; do     ## find files & subdirs below datapath
    if [ -d "$f" ]; then        ## am I a directory that isn't datapath
        [ "$f" != "$datapath/" ] && printf "directory: %s\n" "$f"
    elif [ -f "$f" ]; then      ## am I a file?
        printf "filename : %s\n" "$f"
    else                        ## am I neither a file or directory?
        printf "unknown  : %s\n" "$f"
    fi
done

Теперь, если вы хотите толькоотносительные имена файлов ниже "$dataPath", вам просто нужно обрезать "dataPath/" с начала каждого имени файла, используя расширение параметра ${parameter#[word]} (где [word] расширяется до шаблона, который вы хотите сопоставить, например, "$dataPath/" здесь):

printf "\nwith relative path\n\n"
## output relative file/directory names
for f in "$datapath"/**; do
    relpath="${f#$datapath/}"   ## trim $datapath/ from f
    if [ -d "$f" ]; then
        [ -n "$relpath" ] && printf "directory: %s\n" "$relpath"
    elif [ -f "$f" ]; then
        printf "filename : %s\n" "$relpath"
    else
        printf "unknown  : %s\n" "$relpath"
    fi
done

Пример, включающий оба выше, в следующей структуре каталогов:

Пример каталога

$ tree ~/dev/src-c/tmp/tst/sha2dcr
/home/david/dev/src-c/tmp/tst/sha2dcr
├── chkpass.c
├── dat
│   ├── pw
│   ├── users.bin
│   ├── users.bin.sav
│   └── users.txt
├── getpass.c
├── getpass.h
├── readuserdb.c
├── sha256d.c
├── sha256d.h
├── sha256dtst.c
├── sha256fread.c
├── sha256fread_timed.c
└── userdb.c

Пример сценария Использование / Вывод

Включая как абсолютные, так и относительные пути, вы получите:

$ bash ~/scr/utl/filelist.sh ~/dev/src-c/tmp/tst/sha2dcr
filename : /home/david/dev/src-c/tmp/tst/sha2dcr/chkpass.c
directory: /home/david/dev/src-c/tmp/tst/sha2dcr/dat
filename : /home/david/dev/src-c/tmp/tst/sha2dcr/dat/pw
filename : /home/david/dev/src-c/tmp/tst/sha2dcr/dat/users.bin
filename : /home/david/dev/src-c/tmp/tst/sha2dcr/dat/users.bin.sav
filename : /home/david/dev/src-c/tmp/tst/sha2dcr/dat/users.txt
filename : /home/david/dev/src-c/tmp/tst/sha2dcr/getpass.c
filename : /home/david/dev/src-c/tmp/tst/sha2dcr/getpass.h
filename : /home/david/dev/src-c/tmp/tst/sha2dcr/readuserdb.c
filename : /home/david/dev/src-c/tmp/tst/sha2dcr/sha256d.c
filename : /home/david/dev/src-c/tmp/tst/sha2dcr/sha256d.h
filename : /home/david/dev/src-c/tmp/tst/sha2dcr/sha256dtst.c
filename : /home/david/dev/src-c/tmp/tst/sha2dcr/sha256fread.c
filename : /home/david/dev/src-c/tmp/tst/sha2dcr/sha256fread_timed.c
filename : /home/david/dev/src-c/tmp/tst/sha2dcr/userdb.c

with relative path

filename : chkpass.c
directory: dat
filename : dat/pw
filename : dat/users.bin
filename : dat/users.bin.sav
filename : dat/users.txt
filename : getpass.c
filename : getpass.h
filename : readuserdb.c
filename : sha256d.c
filename : sha256d.h
filename : sha256dtst.c
filename : sha256fread.c
filename : sha256fread_timed.c
filename : userdb.c

( note: вывод directory: /home/david/dev/src-c/tmp/tst/sha2dcr/ равенподавлено в обоих случаях)

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

(примечание: я использую все имена переменных в нижнем регистре, если у вас есть необъяснимая ошибка копирования ...)

0 голосов
/ 25 января 2019

Непосредственная проблема заключается в том, что f задается только для имени файла, а не для всего пути.То есть, если / tmp / testFolder содержит файл с именем example.txt, для f будет установлено значение «example.txt», поэтому if [ -d $f ]; then проверяет файл с именем «example.txt» в текущем рабочем каталоге., не в / tmp / testFolder.Один из вариантов - использовать if [ -d $dirPath/$f ]; then, но есть и лучший способ.

Как правило, вы не должны анализировать вывод ls, поскольку его вывод неоднозначен внесколько путей.Чтобы получить список файлов в определенном каталоге, просто используйте dirpath/* - он получает список подходящих файлов без каких-либо проблем с неоднозначностью или синтаксическим анализом, которые будут у вас с ls.Бонус: он включает указанный путь как часть результата (например, /tmp/testFolder/* может дать "/tmp/testFolder/example.txt").

Другое предложение: вы должны (почти) всегда ставить переменнуюссылки в двойных кавычках, например "$f" вместо просто $f.

Исправление всего этого дает:

for f in "$dirPath"/*; do    # Note that $dirPath should be quoted, but * cannot be
   echo "$f out of if"
   if [ -d "$f" ]; then
      echo "$f is a directory"
   fi
   if [ -f "$f" ]; then
      echo "$f is a file"
   fi
done

Обратите внимание, что команды echo также дадут полныйдорожка.Если вы этого не хотите, вы можете либо использовать команду basename, чтобы получить только часть имени, например, f_name="$(basename "$f")", либо (как указывалось @melpomene) использовать расширение "${f##*/}" (которое урезается до последнего«/» в переменной):

for f in "$dirPath"/*; do
   f_name="${f##*/}"    # The quotes are not strictly needed in an assignment, but do no harm
   echo "$f_name out of if"
   if [ -d "$f" ]; then
      echo "$f_name is a directory"
   fi
   if [ -f "$f" ]; then
      echo "$f_name is a file"
   fi
done

О, и есть один возможный недостаток использования подстановочного знака вместо ls: он вернет необработанный подстановочный знак, если совпадений нет.В данном случае это не имеет значения, но для мест, где это происходит, вы можете либо запустить цикл с [ -e "$f" ] || continue (т.е. пропустить цикл, если на самом деле там ничего нет), либо если вы используете bash (не просто универсальную оболочку) вы можете установить параметр оболочки nullglob (shopt -s nullglob).

Еще одна рекомендация: shellcheck.net хорош для выявления распространенных ошибок сценариев, поэтому я рекомендую запускать ваши сценарии черезэто для предложений.

0 голосов
/ 25 января 2019

Проблема в том, что $f - это относительный путь. Итак, $f - это test.file, но вы хотите проверить /tmp/testFolder/test.file. Правильное решение будет:

for f in "$dirPath"/*; do
   echo "$f out of if"
   if [ -d "$f" ]; then
      echo "$f is a directory"
   fi
   if [ -f "$f" ]; then
      echo "$f is a file"
   fi
done
0 голосов
/ 25 января 2019

Код в if не выполняется, потому что в текущем каталоге нет namefile1, что проверяет ваш код.То есть $f не включает $dirPath (префикс каталога), как написан ваш код.

Лучше:

for f in "$dirPath"/*; do
    echo "$f out of if"
    if [ -d "$f" ]; then
        echo "$f is a directory"
    fi
    if [ -f "$f" ]; then
        echo "$f is a file"
    fi
done

См. Также следующий список ошибок bash:

0 голосов
/ 25 января 2019

Проблема с вашим кодом в том, что переменная f не содержит полный путь к файлу (файлам) в вашей целевой директории.Поэтому, если вы не укажете текущий каталог, тесты «directory» и «file» всегда будут терпеть неудачу.

Вы можете поставить префикс $dirPath в ваших if выражениях (таких как if [ -d "$dirPath/$f" ]; then echo "$dirPath/$f is a directory"; fi),Или, что еще лучше, используйте расширение glob в цикле for, которое даст вам полный путь к каждому файлу.

#!/bin/bash

for f in "$dirPath"/*; do
   if [ -d "$f" ]; then
      echo "$f is a directory"
   elif [ -f "$f" ]; then
      echo "$f is a file"
   fi
done
...