найти -exec в скрипте bash с расширением переменной


14

Я пытаюсь запустить команду, похожую на приведенную ниже, в скрипте bash. Он должен искать во всех подпапках $sourcedirи копировать все файлы определенного типа на корневой уровень $targetdir.

#!/bin/bash

# These are set as arguments to the script, not hard-coded
sourcedir="/path/to/sourcedir"
targetdir="/path/to/targetdir"

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2/`basename "$1"`"' "{}" "$targetdir" \;

Это, кажется, довольно близко, за исключением того, что {}не передается в $2отношении-exec sh -c ...

Я хотел бы сделать это как можно ближе к «правильному пути», с допуском для специальных символов в именах файлов (особенно символы одинарных кавычек).

Изменить: я вижу людей, предлагающих использовать xargsили цепочки аргументов. У меня сложилось впечатление, что это нормально только для ограниченного числа аргументов. Если у меня есть, например, тысячи файлов .jpg, которые я пытаюсь скопировать из нескольких каталогов галерей в гигантский каталог слайд-шоу, будут ли работать решения, объединяющие аргументы?

Изменить 2: Моя проблема заключалась в том, что я пропустил _перед моей первой опцией sh в -execкоманде. Для тех, кому интересно, как заставить команду find работать, добавьте _и все будет хорошо:

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2"' _ "{}" "$targetdir" \;

Я принял ответ ниже, потому что он выполняет ту же задачу, но более эффективен и элегантен.


4
Именно поэтому он и xargsбыл создан, чтобы автоматически обрабатывать огромное количество аргументов в обычных командах, имеющих ограничения. Также следует учитывать, что большинство пределов максимального аргумента были значительно улучшены для стандартных утилит GNU. Вы также увидите выигрыш в производительности, избегая всех тех разветвлений процессов, которые важны для тысяч файлов.
JM Becker

С помощью gnu-find и + вместо «;» вы также можете обрабатывать несколько аргументов одновременно с помощью find. И вы сохраняете сложный аргумент, передавая -print0.
пользователь неизвестен

@userunknown: я отвечаю на это ниже вашего ответа.
Дж. М. Беккер

@ пользователь неизвестен Ну, я люблю этот код. Он по крайней мере полностью POSIX-совместимый и будет работать без каких-либо GNU-компонентов на компьютере. Есть моменты, когда вам это нужно, особенно на серверах на работе.
syntaxerror

Ответы:


6

Вы хотите скопировать файлы определенного типа в определенный каталог? Лучше всего это сделать xargs, а вам это даже не нужно sh. Это более подходящий способ сделать это, должен также работать более эффективно.

find "$sourcedir" -type f -name "*.type" | xargs cp -t targetdir

Если вам нужно обрабатывать специальные имена файлов, используйте их NULLкак разделитель

find "$sourcedir" -type f -name "*.type" -print0 | xargs -0 cp -t "$targetdir"

1
Для 2 - го корпуса, не забудьте добавить -print0в findи -0кxargs
SiegeX

@ SiegeX, уже делал это, прежде чем я заметил твой комментарий.
JM Becker

Кроме того, NULLнет необходимости, если вы используете '{}', важно, когда вы не используете. Реальная выгода между одним из других, помимо POSIXсоблюдения, является производительность.
JM Becker

6

Вам нужно передать {}в качестве аргумента оболочку, а затем выполнить цикл по каждому аргументу.

find "$sourcedir" -type f -name "*.type" -exec sh -c 'for f; do cp "$f" "$0"; done' "$targetdir" {} +

Примечание : способ, которым это работает, заключается в том, что первым аргументом для оболочки является имя оболочки , мы можем использовать это, передавая имя как $targetdirи затем используя специальный параметр $0внутри сценария оболочки для доступа к этому targettdir.


1
"$targetdir"не раскрывается внутри одинарных кавычек.
энзотиб

5

Если вы не верите в церковь Xargs:

find "$sourcedir" -type f -name "*.mp3" -exec cp -t "$targetdir" {} +

Объяснение:

cp -t a b c d 

копирует b, c и d в целевой каталог a.

-exec cmd {} +

вызывает команду для большого количества файлов сразу, а не один за другим (что является стандартным, если вы используете ";"вместо +). Вот почему вы должны вытащить targettdir вперед и отметить его как цель.

Это работает для gnu-find и может не работать для других реализаций find. Конечно, это также зависит от -t -flag.

TechZilla, конечно, так далеко, так как shне нужен для вызова cp.

Если вы не используете xargs, что большая часть времени ненужного в сочетании с find, вы спасены от обучения -print0и -0флага.


1
Видимо, какой метод кто-то может предпочесть, это немного субъективно. С учетом сказанного, существуют причины, по-прежнему использовать xargs. Самый большой пример, что делать, если вы не находите файлы? Я все время использую xargs вместо общих forциклов, findгораздо меньше по объему. Кроме того, findподдерживается только то, что +если вы используете GNU find, это не определено в POSIX. Так что, хотя вы не стесняйтесь предпочитать в findодиночку, он делает не все, что делает xargs Когда вы рассматриваете GNU, xargsвы также получаете -Pмногоядерный процессор. Это стоит учиться xargsнезависимо.
Дж. М. Беккер

Субъективно говоря, мое мнение, очевидно, отличается. С другой стороны, ваш ответ также верен. Это одно из немногих лучших доступных решений.
Дж. М. Беккер

@TechZilla: Я надеюсь не забыть вернуться к этому сайту, когда find начнет поддерживать параллельный вызов. :) В большинстве случаев при копировании / перемещении скорость диска будет ограничивающим фактором, но SSD может изменить картину. Вы правы, что не-GNU-решение - это хорошая вещь. Иначе, решение find объективно короче, проще и использует только два процесса.
неизвестный пользователь

0
read -p "SOURCE: " sourcedir
read -p "TYPE: " type
read -p "TARGET: " targetdir
find -L $sourcedir -iname "*.$type" -exec cp -v {} $targetdir \;

3
Попробуйте добавить некоторые объяснения вашего решения.
HalosGhost
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.