Я изучаю сценарии bash, и мне нужна была простая помощь.
Вот что у меня есть на данный момент:
find . -type d -empty -not -path "./.git/*" -exec touch {}/.gitkeep \;
Таким образом, это начинается с корневого пути, находит все каталоги внутри этого корневого пути, которые пусты и не имеют папки .git, а затем, когда эта операция успешна, запускает -exec touch {}/.gitkeep для создания файла .gitkeep внутри этого пустого каталога, чтобы гарантировать правильные коммиты git.
Теперь я хочу, чтобы echo извлек текущий путь к только что созданному файлу gitkeep.
Мой первый вопрос:
Стоит ли прокладывать | так:
find . -type d -empty -not -path "./.git/*" -exec touch {}/.gitkeep | outputFilenameDisplayFunction \;
Или, может быть, повторить то, что делает -exec, вот так:
find . -type d -empty -not -path "./.git/*" -exec touch {}/.gitkeep - exec outputFilenameDisplayFunction \;
А может использовать >
find . -type d -empty -not -path "./.git/*" -exec touch {}/.gitkeep > outputFilenameDisplayFunction \;
Ни одна из этих команд еще не тестировалась. Я действительно ищу объяснения, чтобы я мог быть осведомленным в будущем.
... кстати, если вы действительно хотите передать что-то в функцию - поскольку -exec запускает программу напрямую (без оболочки), он не может напрямую запускать функцию оболочки (которая по своей природе существует только тогда, когда вы иметь оболочку в первую очередь). Вы может запускаете функцию оболочки под -exec, если вы экспортируете ее с помощью export -f и гарантируете, что дочерняя оболочка - это bash, но это довольно трудоемкая работа и, как правило, не лучший подход для работы.
В общем, я бы посоветовал обратиться к методам в Использование поиска - обратите внимание, в частности, на использование потоков с разделителями NUL для передачи имен из find в список bash while read (метод, обсуждаемый также в BashFAQ # 1).
Кстати, -exec touch '{}/.gitkeep' не гарантированно будет работать везде - спецификация POSIX для find только обещает, что {} соблюдается, когда это отдельный аргумент сам по себе, а не когда он является подстрокой другого аргумента, поэтому операционные системы с более простыми реализациями find могут не веди себя здесь так, как собираешься.





Как упомянутый здесь, find принимает несколько частей -exec в команду.
В вашем случае второй может вызывать скрипт как здесь:
find . -type d -empty -not -path "./.git/*" -exec touch {}/.gitkeep \; -exec myscript {} \;
Обратите внимание на \;.
Сценарий будет:
#!/bin/sh
echo "$1" > "afile"
Чарльз Даффи фактически предлагает в комментариях для второго -exec:
-exec sh -c 'echo "$1" >>aFile' _ {} \;
avoid the need for an external file storing your script.
Сохранять -path './.git/*' - к сожалению - это довольно неэффективная конструкция по сравнению с использованием -prune.
@CharlesDuffy Я согласен. Я просто опирался на базовую команду, предложенную ОП.
Кстати, я считаю, что прямо сейчас здесь есть несколько опечаток, связанных с пробелами - по-видимому, вы имеете в виду, что перед \; должен быть пробел, а между следующими - и exec не должно быть пробелов.
@CharlesDuffy Верно, исправлено.
LG TM, хотя лично я мог бы использовать -exec sh -c 'echo "$1" >>aFile' _ {} \; или что-то подобное и избежать необходимости во внешнем файле, в котором хранится ваш сценарий.
@CharlesDuffy Хорошее замечание. Раньше я пропустил stackoverflow.com/a/25507672/6309.
Начнем с заявленных вами требований:
So what this does is starts from a root path, finds all directories inside this root path that are empty and do not have a .git folder, and then when that operation is successful it runs -exec touch {}/.gitkeep to create a file .gitkeep inside that empty directory to ensure proper git commits.
Если каталог пуст, он «не может иметь папку .git» в смысле наличия дочернего элемента с именем .gitпо определению - если бы в нем был какой-либо подкаталог, он не был бы пустым. Таким образом, мы можем полностью игнорировать эту часть вашего описания в прозе - или интерпретировать как ссылку на код на самом деле, похоже, предназначен для, удаляя любой каталог, который является под.git.
Если это будет вашим намерением, -path - совсем неподходящий инструмент для этой работы, поскольку он по-прежнему выполняет поиск в дереве .git (а затем исключает все найденные элементы); вместо этого используйте -prune, чтобы find вообще не возвращался по этому пути:
while IFS= read -r -d '' dirname; do
touch -- "${dirname}/.gitkeep"
printf '%q\n' "$dirname" # this goes to the logfile, since we open it for the whole loop
done < <(find . -name .git -prune -o -type d -empty -print0) >logFile
Почему предпочитаю этот подход?
-exec для запуска сценария оболочки или оболочки), он поддерживает работу вашей начальной / основной оболочки и выполняет итерацию цикла один раз для каждого найденного элемента.(( ++directoriesFound )), чтобы вести счетчик, f / e) или выполнять перенаправления в пределах цикла (например, >logFile), чтобы открыть выходной файл только один раз и повторно использовать if внутри.Это определенно лучше, чем использование -path. +1
В GNU / something find имеет -printf, что делает то, что вы хотите, прямым
find -name .git -prune \
-o -type d -empty -printf %p/.gitkeep\\n -execdir touch {}/.gitkeep \;
(примечание: исправлено опущенное {}/, а -execdir GNU find не меняет здесь поведения, но он безопаснее, чем -exec, в системах, которые могут оказаться под атакой, команда exec'd запускается непосредственно в том месте, куда попал find, а не вызывает выполненная команда для повторного прохождения пути).
Re:
> outputFilenameDisplayFunction- перенаправления интерпретируются оболочкой еще до запускаfind. Следовательно, это не часть последовательности-exec(когда у вас есть оболочка, которая учитывает их в положении без головы или хвоста, как это делает bash); и даже если вы процитировали его, как в'>outputFilenameDisplayFunction', такая идиома - перенаправление оболочки; когда у вас вообще нет оболочки (а-execне запускает новую оболочку самостоятельно), нет ничего, что могло бы ее уважать.