найти с -execdir

15

Когда я бегу findс собой, -execdirя не получаю ожидаемых результатов.

Например:

mkdir -p a/b/c
find . -type d -execdir touch foo \;
$ tree a
a
├── b
   ├── c
   └── foo
└── foo

Каталог cне содержит fooфайл. Как я могу findпосетить и сделать что-то локально в каждом каталоге?

Маркус Юний Брут
источник

Ответы:

18

Для каждого соответствующего файла (т. Е. Каждого каталога) findпереключается на каталог, в котором он находится (т. Е. Его родительский каталог), и выполняет указанную команду. Поскольку команда не использует имя совпадения, она никогда не будет действовать на все каталоги. Для этого конкретного дерева каталогов вы делаете

(cd . && touch foo)        # because ./a matches
(cd ./a && touch foo)      # because ./a/b matches
(cd ./a/b && touch foo)    # because ./a/b/c matches

Чтобы создать файл в каждом каталоге, вы можете просто использовать -execвместо -execdir, при условии, что ваша реализация findдопускает {}внутри аргумента (большинство делают, и, в частности, я думаю, все):

find . -type d -exec touch {}/foo +

Для переносимости POSIX вам необходимо выполнить сборку имени каталога и базового имени файла вручную.

find . -type d -exec sh -c 'touch "$0/foo"' {} \;

или (немного быстрее)

find . -type d -exec sh -c 'for d; do touch "$d/foo"; done' _ {} +

Кроме того, вы можете использовать рекурсивное сопоставление с подстановочными знаками в bash. Помните, что (в отличие от соответствующей функции в ksh и zsh и в отличие от вашей findкоманды) bash рекурсивно используется по символическим ссылкам на каталоги.

shopt -s globstar
for d in **/*/; do touch -- "$d/foo"; done

Zsh решение:

touch ./**/(e\''REPLY+=foo'\')
Жиль "ТАК - перестань быть злым"
источник
FYI: man bashсостояния в «-c»: аргументы после command_string назначаются позиционным параметрам, начинающимся с $ 0, однако «для d» будет перебирать позиционные параметры, начинающиеся с $ 1. «_» - это текст, который назначен на $ 0 и не будет использоваться.
Чад Скитерс
3

Команда выполняется в каждом каталоге, который содержит соответствующий файл. Поскольку cкаталог не содержит, он не совпадает и, следовательно, не будет работать там.

Решением является добавление имени каталога в аргумент execdir, например так:

find . -type d -execdir touch {}/foo \;
Дженни Д
источник
2

Из man file

   -execdir command {} +
          Like  -exec,  but  the  specified  command is run from the subdirectory containing the matched file

Ваш соответствующий каталог c в bкаталоге, так что отсюда запускается exec. Это будет работать, как вы ожидаете, если вы ищете файлы вместо каталогов.

Вы, вероятно, могли бы достичь того, чего хотите, отправив каталоги, так xargsкак при этом будет предоставлен полный список каталогов.

Matt
источник