Сложная команда поиска с несколькими сокращениями

Мне нужно пройтись по дереву каталогов с несколькими начальными точками, например, find ./User1 ./User2 ./User3 но обработать только определенные имена каталогов, которые существуют в любой из этих начальных точек, например: ./User1/Documents ./User1/Pictures /.User2/Documents и т. Д. Я хотел бы пропустить и prune любые другие подпапки, не /.User2/Documents в данный список, из всех начальных точек.

Все мои попытки нескольких комбинаций -not и prune с \( & \) потерпели неудачу.

Начальные точки будут определены в скрипте во время выполнения. В крайнем случае, я мог бы составить список начальных точек, перебрав список User? и создание еще большего списка начальных точек с уже добавленными желаемыми именами каталогов.

Только ./User?/NotInList должен быть исключен, но ./User?/InList/NotInList в порядке.

[Общая картина: я делаю восстановление данных и хочу перечислить «важные» файлы в папке « Users Microsoft Windows». Мне нужно пропустить папки Public и Default (поэтому я создаю строку из оставшихся папок в качестве отправных точек для команды find ), а также только выбирать / опускаться в папки Pictures Documents и т. Д. Каждого пользователя, но не в AppData или Contacts и т. д. папки, которые редко являются важными.]

3 Solutions collect form web for “Сложная команда поиска с несколькими сокращениями”

С zsh :

 users=(User1 User2) # or users=($( 

То же самое с fish :

 set users User1 User2 ... set dirs Documents Pictures find ./$users/$dirs 

В fish или zsh -o rcexpandparam массивы расширяются zsh -o rcexpandparam расширения скобок. В zsh синтаксис $^array включает rcexpandparam для этого расширения одного массива.

Ссылка на rc здесь немного вводит в заблуждение. В то время как $array^string в rc / es (где array (1 2)) будет расширяться как {1,2}string (и именно поэтому zsh выбрал ^ для своего rc-подобного типа расширения в $^array ), это не относится к объединению массивов. В rc $array1^$arrat2 (аналогично $array1$array2 ) работает только для массивов одинакового размера и соединяет элемент один за другим ( (1 2)^(ab) становится 1a 2b , а не 1a 1b 2a 2b ) ,

Обратите внимание, что они не глобальные , User1/Documents будет передан для find независимо от того, существует этот файл или нет. Чтобы передать список тех файлов или каталогов, которые действительно существуют , в zsh , вы можете сделать

 find ./$^users/$^dirs(N) 

который передает добавляет (N) квалификатор glob ко всем элементам, полученным в результате умножения этого массива, что имеет два эффекта:

  • делает их глобусами, что означает, что они будут расширяться до соответствующих файлов
  • если глобус не соответствует ни одному файлу (они могут соответствовать только 0 или 1, так как там нет подстановочных знаков), результирующий глобус вместо этого расширяется в ничто.

В качестве альтернативы, вы можете пройти весь путь:

 find (User1|User2)/(Documents|Pictures) 

Или на основе вашего примера:

 set -o extendedglob # best in ~/.zshrc find ./^(#i)(Default|Public)/(Documents|Pictures) 

Или сгенерируйте этот глобус на основе массивов с

 find (${(j:|:)~${(b)users}})/${(j:|:)~${(b)dirs}} 

Куда:

  • (b) заключает в кавычки операторы подстановки, если таковые имеются в элементах массива
  • (j:|:) соединяет их с |
  • ~ включает в себя шататься на расширение

Они работают с произвольными именами файлов (кроме тех, которые начинаются с - как ограничение find ).

Это было бы очень сложно, особенно если вы хотите разрешить произвольные имена файлов. Но для ваших относительно прирученных, таких как Default , Public ...), вы можете попробовать:

 LC_ALL=C find . -name . -o -path './*/*' \( ! -path './*/*/*' \ ! -name Documents ! -name Pictures -prune -o -print \) -o \ ! -name Default ! -name Public -o -prune 

(Подобные вещи вызывают у меня головную боль).

Это использует стандартный синтаксис find , если вы хотите, чтобы регистр не -name '[pP][uU][bB][lL][iI][cC]' , вы можете использовать -name '[pP][uU][bB][lL][iI][cC]' , или если вы используете GNU find (который вам кажется ) или совместимый, используйте -iname Public .

Судя по ответу, который вы опубликовали самостоятельно, вы хотите выбрать каталоги ./ x / y , где x – это любой из каталогов в текущем каталоге, кроме Public или Default , а y – любой из Documents , Pictures или My Documents .

В Bash вы можете сделать это с помощью glob, когда включен extglob . (В ksh это значение по умолчанию, когда шаблон находится непосредственно в командной строке; в Zsh вам нужно использовать setopt kshglob .)

Дано:

 $ mkdir -p {Someuser,"other user",Public,Default}/{Pictures,"My Documents",Uselessdir} 

шар должен делать то, что вы хотите:

 $ shopt -s extglob $ printf "%s\n" ./!(Public|Default)/@(Documents|My Documents|Pictures) ./other user/My Documents ./other user/Pictures ./Someuser/My Documents ./Someuser/Pictures 

Итак, вы должны быть в состоянии просто запустить

 shopt -s extglob find ./!(Public|Default)/@(Documents|My Documents|Pictures) ... 

Глобус не будет распространяться на каталоги, которые не существуют, поэтому вы не получите ошибок от find если скажем ./Someuser/Pictures не существует.

Добавьте shopt -s nocaseglob если хотите найти регистр shopt -s nocaseglob регистра.

Основываясь на комментарии @StephaneChazelas, используя расширение скобок, вот решение, которое я выбрал для bash который работает с пробелами в именах файлов и обрабатывает крайние случаи, как только один пользователь.

Строка для расширения скобки будет храниться в переменной, поэтому нам нужно использовать eval для ее фактического расширения.

 # escape spaces with backslash directories=Documents,Pictures,My\ Documents # get usernames to use with expansion # and escape any spaces using sed user_folders=$(find . -maxdepth 1 -mindepth 1 -not -iname Public -a -not -iname Default -type d -printf "%f," | \ sed 's| |\\ |g' ) # remove trailing comma from last user's name user_folders=${user_folders::-1} # only use brace expansion if there is more than one user; if there's only # one value it won't expand and we'll be left with braces [[ $user_folders = *,* ]] && user_folders="{$user_folders}" find_string="find $user_folders/{$directories}" eval "$find_string" 
  • Почему эта команда работает только для каждой другой строки?
  • Почему `* .ext` работает при поиске файла с расширением 'ext'
  • BASH не нравится мое регулярное выражение
  • vim: команда для удаления точки с запятой, если это последний символ строки
  • Regex для форматирования вывода файлов
  • Как использовать sed и регулярные выражения, чтобы найти шаблон и удалить последние несколько символов?
  • Grep регулярное выражение для отображения только серийных номеров (определенной длины, содержащих альфа и цифру) в файле CSV
  • найти + распечатать файлы с отметкой времени
  • Соответствие и в строке с несколькими столбцами в unix
  • Пакетное переименование папок со значением из значения json в package.json
  • проверьте обновление ping для большого количества машин
  • Interesting Posts

    Компилятор C не может создавать исполняемые файлы при установке h4toh5 на debian

    Строка Grep, затем удалите слово и выполните следующую инструкцию

    Docker – Дайте переменную для создания докеров

    Какой OID для проверки вывода сценария extend-sh в Net-SNMP snmpd?

    Почему этот случайный пароль помечен, говоря, что он слишком упрощен / систематичен?

    Как сохранить пару ключей имени пользователя и пароля в SSH в GIT Bash?

    Невозможно открыть синтаксическую ошибку при использовании vimrc

    В чем смысл конечного «:» (двоеточия) в * nix?

    Каким будет пакет RHEL, соответствующий встроенному в Ubuntu?

    изменение разделителей столбцов в файле

    wc -l файл переменной

    настройка setxkbmap для изменения одного пользователя / отладки xkbmap

    Сервер Linux не принимает TCP-соединения

    Как удалить точку и пробел с начала имен файлов

    Список пакетов, исходящих из определенного Codename или Suite (основанные на apt системах)

    Linux и Unix - лучшая ОС в мире.