Рекурсивно выполните итерации через файлы в каталоге

getopt и getopts поддерживайте любой порядок на данные аргументы - Они просты в использовании, и, кажется, канонические решения парсинга аргумента.

Два различия, которые часто упоминаются:

  • getopt поддержки долгие опции как --help.
  • getopts встроенная оболочка Bash, а не автономная программа.

Упрощенный обширный пример:

# Process parameters
params="$(getopt -o e:hv \
    -l exclude:,help,verbose \
    --name "$0" -- "$@")"

if [ $? -ne 0 ]
then
    usage
fi

eval set -- "$params"
unset params

while true
do
    case $1 in
        -e|--exclude)
            excludes+=("${2-}")
            shift 2
            ;;
        -h|--help)
            usage
            exit
            ;;
        -v|--verbose)
            verbose='--verbose'
            shift
            ;;
        --)
            shift
            if [ -z "${1:-}" ]
            then
                error "Missing targets." "$help_info" $EX_USAGE
            fi
            if [ -z "${2:-}" ]
            then
                error "Missing directory." "$help_info" $EX_USAGE
            fi
            targets=(${@:1:$(($#-1))})
            source_dir="${@:$#}"
            break
            ;;
        *)
            usage
            ;;
    esac
done

15
26.06.2014, 17:01
4 ответа

Еще одно использование для safe find:

while IFS= read -r -d '' -u 9
do
    [Do something with "$REPLY"]
done 9< <( find . -type f -exec printf '%s\0' {} + )

(Это работает с любыми POSIX find, но часть оболочки требует bash. С помощью *BSD и GNU find можно использовать -print0 вместо -exec printf '%s\0' {}. +, будет немного быстрее.)

Это позволяет использовать стандартный вход в цикле, и работает с любым путем.

.
7
27.01.2020, 19:49

Делать это так же просто, как:

find -exec sh -c 'inline script "$0"' {} \;

Или...

find -exec executable_script {} \;
9
27.01.2020, 19:49

Самый простой (но безопасный) подход - это использование shell-глобуса:

$ for f in *; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
h:

Для того, чтобы сделать вышеуказанный рекурсив в подкаталогах (в bash), вы можете использовать опцию globstar; также установите dotglob для сопоставления файлов, чье имя начинается с . :

$ shopt -s globstar dotglob
$ for f in **/*; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
:foo:
:foo/file1:
:foo/file two:
h:

Остерегайтесь, что до bash 4.2, **/ возвращается в символические ссылки на каталоги. Начиная с версии 4.3 bash, **/ возвращается только в каталоги, как find.

Другое распространенное решение - использовать find -print0 с xargs -0:

$ touch -- 'a b' $'c\nd' $'e\tf' $'g\rh' '-e'
$ find . -type f -print0 | xargs -0 -I{} printf ":%s:\n" {}
h:/g
:./e    f:
:./a b:
:./-e:
:./c
d:

Обратите внимание, что h:/g на самом деле корректен, так как имя файла содержит \r.

.
5
27.01.2020, 19:49

Немного сложно сделать цикл чтения портативно, но для bash в частности можно попробовать что-то вроде this.

Соответствующая часть:

while IFS= read -d $'\0' -r file ; do
        printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)

, которая инструктирует найти, чтобы распечатать его вывод, разделенный NUL-символами (0x00), и прочитать, чтобы получить NUL-разделенные строки (-d $'\0'), не обращаясь к обратным слешам как к экранированию для других символов (-r) и не делайте никакого разделения слов на строках (IFS=). Так как 0x00 - это байт, который не может встречаться в именах файлов или путях в Unix, это должно решить все ваши странные проблемы с именами файлов.

.
4
27.01.2020, 19:49

Теги

Похожие вопросы