Я хочу использовать его find
для поиска файлов в наборе папок, ограниченных подстановочными знаками, но где в имени пути есть пробелы.
Из командной строки это просто. Все следующие примеры работают.
find te*/my\ files/more -print
find te*/'my files'/more -print
find te*/my' 'files/more -print
Они найдут файлы, например, в форматах terminal/my files/more
и tepid/my files/more
.
Однако мне нужно, чтобы это было частью сценария; мне нужно что-то вроде этого:
SEARCH='te*/my\ files/more'
find ${SEARCH} -print
К сожалению, что бы я ни делал, похоже, я не могу смешивать подстановочные знаки и пробелы в find
команде внутри скрипта. Приведенный выше пример возвращает следующие ошибки (обратите внимание на неожиданное удвоение обратной косой черты):
find: ‘te*/my\\’: No such file or directory
find: ‘files/more’: No such file or directory
Попытка использовать кавычки также не увенчалась успехом.
SEARCH="te*/'my files'/more"
find ${SEARCH} -print
Это возвращает следующие ошибки, игнорируя значение кавычек:
find: ‘te*/'my’: No such file or directory
find: ‘files'/more’: No such file or directory
Вот еще один пример.
SEARCH='te*/my files/more'
find ${SEARCH} -print
Как и ожидалось:
find: ‘te*/my’: No such file or directory
find: ‘files/more’: No such file or directory
Все варианты, которые я пробовал, возвращали ошибку.
У меня есть обходной путь, который потенциально опасен, поскольку возвращает слишком много папок. Я преобразую все пробелы в вопросительный знак (односимвольный подстановочный знак) следующим образом:
SEARCH='te*/my files/more'
SEARCH=${SEARCH// /?} # Convert every space to a question mark.
find ${SEARCH} -print
Это эквивалентно:
find te*/my?files/more -print
Это возвращает не только правильные папки, но и terse/myxfiles/more
, что не должно быть так.
Как мне добиться того, чего я пытаюсь добиться? Google мне не помог :(
решение1
Точно такая же команда должна работать в скрипте:
#!/usr/bin/env bash
find te*/my\ files/ -print
Если вынуждатьсяесли использовать его как переменную, то все становится немного сложнее:
#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print
ПРЕДУПРЕЖДЕНИЕ:
Использование eval
подобных конструкций небезопасно и может привести к выполнению произвольного и потенциально вредоносного кода, если имена ваших файлов могут содержать определенные символы. См.Часто задаваемые вопросы по bash 48для получения подробной информации.
Лучше передать путь в качестве аргумента:
#!/usr/bin/env bash
find "$@" -name "file*"
Другой подход — полностью избегать find
и использовать расширенные возможности подстановки bash и подстановки:
#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done
Опция globstar
bash позволяет использовать **
для рекурсивного сопоставления:
globstar
If set, the pattern ** used in a pathname expansion con‐
text will match all files and zero or more directories
and subdirectories. If the pattern is followed by a /,
only directories and subdirectories match.
Чтобы он действовал на 100% как поиск и включение dotfiles (скрытых файлов), используйте
#!/usr/bin/env bash
shopt -s globstar
shopt -s dotglob
for file in te*/my\ files/**; do echo "$file"; done
Вы можете выровнять echo
их напрямую, без цикла:
echo te*/my\ files/**
решение2
А как насчет массивов?
$ tree Desktop/ Documents/
Desktop/
└── my folder
└── more
└── file
Documents/
└── my folder
├── folder
└── more
5 directories, 1 file
$ SEARCH=(D*/my\ folder)
$ find "${SEARCH[@]}"
Desktop/my folder
Desktop/my folder/more
Desktop/my folder/more/file
Documents/my folder
Documents/my folder/more
Documents/my folder/folder
(*)
расширяется в массив всего, что соответствует подстановочному знаку. И "${SEARCH[@]}"
расширяется во все элементы массива ( [@]
), каждый из которых заключен в кавычки.
С опозданием я понимаю, что find сам должен быть способен на это. Что-то вроде:
find . -path 'D*/my folder/more/'
решение3
Сейчас это немного устарело, но, если это может кому-то помочь с этим вопросом, использование символа сортировки RE [[.space.]]
без заключения $SEARCH
переменной в кавычки в аргументах командной строки find работает, пока в расширенных именах путей вместо звездочки нет специальных символов.
set -x
mkdir -p te{flon,nnis,rrine}/my\ files/more
SEARCH=te*/my[[.space.]]files/more
find $SEARCH
дают следующие результаты:
+ mkdir -p 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more'
+ SEARCH='te*/my[[.space.]]files/more'
+ find 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more'
teflon/my files/more
tennis/my files/more
terrine/my files/more
Чтобы предотвратить глобализацию нежелательных символов, можно заменить звездочку ( *
) любыми элементами сортировки (символами):
set -x
mkdir -p -- te{,-,_}{flon,nnis,rrine}/my\ files/more
SEARCH=te[[:alnum:][.space.][.hyphen.][.underscore.]]*/my[[.space.]]files/more
find $SEARCH
давая следующие результаты:
+ mkdir -p -- 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more' \
'te-flon/my files/more' 'te-nnis/my files/more' 'te-rrine/my files/more' \
'te_flon/my files/more' 'te_nnis/my files/more' 'te_rrine/my files/more'
+ SEARCH='te[[:alnum:][.space.][.hyphen.][.underscore.]]*/my[[.space.]]files/more'
+ find 'te-flon/my files/more' 'te_flon/my files/more' 'teflon/my files/more' \
'te-nnis/my files/more' 'te_nnis/my files/more' 'tennis/my files/more' \
'te-rrine/my files/more' 'te_rrine/my files/more' 'terrine/my files/more'
te-flon/my files/more
te_flon/my files/more
teflon/my files/more
te-nnis/my files/more
te_nnis/my files/more
tennis/my files/more
te-rrine/my files/more
te_rrine/my files/more
terrine/my files/more
Обратите внимание, что для сокращения строки
[.hyphen.]
можно заменить на или [.-.]
, -
а
[.underscore.]
можно заменить на [._.]
или _
.
Обрезанные строки с обратными косыми чертами ( \
) добавлены мной для удобства чтения.
решение4
Я наконец нашел ответ.
Добавьте обратную косую черту ко всем пробелам:
SEARCH='te*/my files/more'
SEARCH=${SEARCH// /\\ }
На данный момент SEARCH
содержит te*/my\ files/more
.
Затем используйте eval
.
eval find ${SEARCH} -print
Это так просто! Использование eval
обходит интерпретацию, которая ${SEARCH}
исходит от переменной.