Я пытался заставить это работать какое-то время. Я хочу найти список файлов, а затем скопировать их на определенный путь.
Если я запускаю команду отдельно, то она работает так:
find <path> -name 'file1' -exec cp '{}' <path2> \;
Но Я не смог запустить его в цикле for.
#this works
for f in file1 file2 file3...; do find <path> -name "$f" \; done;
#none of these work (this code tries to find and copy the files named file and list from the path)
for f in file1 file2 file3...; do find <path> -name "$f" -exec cp '{}' <path2> \; done;
for f in file1 file2 file3...; do find <path> -name "$f" -exec cp {} <path2> \; done;
Я пробовал несколько других вещей, которые вряд ли сработают. Первая строка в цитате кода просто застревает, а другие ничего не копируют, даже если они не застревают.
Я не смог запустить что-либо с exec внутри цикла for после поиска, и на данный момент я не уверен, что делать.
Я решил немедленную проблему, выполнив поиск файлов и запустив результаты в другой файл, а затем запустив копию внутри цикла for отдельно, но мне все же хотелось бы знать, как это сделать.
]В то время как другие ответы правильные, я хочу предложить другой подход, который не требует многократных вызовов find для повторного сканирования той же структуры каталога. Основная идея состоит в том, чтобы использовать find для
сгенерировать список файлов, соответствующих общим критериям, применить к этому списку специальный фильтр и выполнить некоторые действия, например. г. cp, о записях отфильтрованного списка.(требует, чтобы Bash считывал записи с нулевым байтом)
find /some/path -type f -print0 |
while read -rd '' f; do
case "${f##*/}" in
file1|file2|file3)
printf '%s\0' "$f";;
esac
done |
xargs -r0 -- cp -vt /some/other/path --
Каждая труба соответствует началу следующего шага три шага, описанные выше.
У оператора case есть преимущество, позволяющее совпадениям с глобусами. В качестве альтернативы вы можете использовать условные выражения Bash:
if [[ "${f##*/}" == file1 || "${f##*/}" == file2 || "${f##*/}" == file3 ]]; then
printf '%s\0' "$f"
fi
Если список имен файлов для соответствия немного длиннее и не может быть заменен небольшим набором [d10 ] globbing или если список имен файлов, которые будут соответствовать, неизвестен на момент написания, вы можете обратиться к массиву, который содержит список интересующих файлов:
FILE_NAMES=( "file1" "file2" "file3" ... )
find /some/path -type f -print0 |
while read -rd '' f; do
for needle in "${FILE_NAMES[@]}"; do
if [ "${f##*/}" = "$needle" ]; then
printf '%s\0' "$f"
fi
done
done |
xargs -r0 -- cp -vt /some/other/path --
В качестве вариации мы можем использовать массив , который, как мы надеемся, имеет более быстрое время поиска, чем обычные массивы «list»:
declare -A FILE_NAMES=( ["file1"]= ["file2"]= ["file3"]= ... ) # Note the superscripts with []=
find /some/path -type f -print0 |
while read -rd '' f; do
if [ -v FILE_NAMES["${f##*/}"] ]; then
printf '%s\0' "$f"
fi
done |
xargs -r0 -- cp -vt /some/other/path --
В то время как другие ответы правильные, я хочу предложить другой подход, который не требует многократных вызовов find для повторного сканирования той же структуры каталога. Основная идея состоит в том, чтобы использовать find для
сгенерировать список файлов, соответствующих общим критериям, применить к этому списку специальный фильтр и выполнить некоторые действия, например. г. cp, о записях отфильтрованного списка.(требует, чтобы Bash считывал записи с нулевым байтом)
find /some/path -type f -print0 |
while read -rd '' f; do
case "${f##*/}" in
file1|file2|file3)
printf '%s\0' "$f";;
esac
done |
xargs -r0 -- cp -vt /some/other/path --
Каждая труба соответствует началу следующего шага три шага, описанные выше.
У оператора case есть преимущество, позволяющее совпадениям с глобусами. В качестве альтернативы вы можете использовать условные выражения Bash:
if [[ "${f##*/}" == file1 || "${f##*/}" == file2 || "${f##*/}" == file3 ]]; then
printf '%s\0' "$f"
fi
Если список имен файлов для соответствия немного длиннее и не может быть заменен небольшим набором globbing или если список имен файлов, которые будут соответствовать, неизвестен на момент написания, вы можете обратиться к массиву, который содержит список интересующих файлов:
FILE_NAMES=( "file1" "file2" "file3" ... )
find /some/path -type f -print0 |
while read -rd '' f; do
for needle in "${FILE_NAMES[@]}"; do
if [ "${f##*/}" = "$needle" ]; then
printf '%s\0' "$f"
fi
done
done |
xargs -r0 -- cp -vt /some/other/path --
В качестве вариации мы можем использовать массив , который, как мы надеемся, имеет более быстрое время поиска, чем обычные массивы «list»:
declare -A FILE_NAMES=( ["file1"]= ["file2"]= ["file3"]= ... ) # Note the superscripts with []=
find /some/path -type f -print0 |
while read -rd '' f; do
if [ -v FILE_NAMES["${f##*/}"] ]; then
printf '%s\0' "$f"
fi
done |
xargs -r0 -- cp -vt /some/other/path --
В то время как другие ответы правильные, я хочу предложить другой подход, который не требует многократных вызовов find для повторного сканирования той же структуры каталога. Основная идея состоит в том, чтобы использовать find для
сгенерировать список файлов, соответствующих общим критериям, применить к этому списку специальный фильтр и выполнить некоторые действия, например. г. cp, о записях отфильтрованного списка.(требует, чтобы Bash считывал записи с нулевым байтом)
find /some/path -type f -print0 |
while read -rd '' f; do
case "${f##*/}" in
file1|file2|file3)
printf '%s\0' "$f";;
esac
done |
xargs -r0 -- cp -vt /some/other/path --
Каждая труба соответствует началу следующего шага три шага, описанные выше.
У оператора case есть преимущество, позволяющее совпадениям с глобусами. В качестве альтернативы вы можете использовать условные выражения Bash:
if [[ "${f##*/}" == file1 || "${f##*/}" == file2 || "${f##*/}" == file3 ]]; then
printf '%s\0' "$f"
fi
Если список имен файлов для соответствия немного длиннее и не может быть заменен небольшим набором globbing или если список имен файлов, которые будут соответствовать, неизвестен на момент написания, вы можете обратиться к массиву, который содержит список интересующих файлов:
FILE_NAMES=( "file1" "file2" "file3" ... )
find /some/path -type f -print0 |
while read -rd '' f; do
for needle in "${FILE_NAMES[@]}"; do
if [ "${f##*/}" = "$needle" ]; then
printf '%s\0' "$f"
fi
done
done |
xargs -r0 -- cp -vt /some/other/path --
В качестве вариации мы можем использовать массив , который, как мы надеемся, имеет более быстрое время поиска, чем обычные массивы «list»:
declare -A FILE_NAMES=( ["file1"]= ["file2"]= ["file3"]= ... ) # Note the superscripts with []=
find /some/path -type f -print0 |
while read -rd '' f; do
if [ -v FILE_NAMES["${f##*/}"] ]; then
printf '%s\0' "$f"
fi
done |
xargs -r0 -- cp -vt /some/other/path --
Два вопроса:
for f in some_file не перебирает содержимое some_file, просто перебирает или принимает литералную строку some_file. Чтобы преодолеть это, забудьте про цикл for для итерации содержимого файла, используйте правильно реализованную конструкцию while. В этом случае переменные не будут расширены при вставке в одинарные кавычки '$f'. Чтобы ваша оригинальная идея работала, используйте двойные кавычки.Объединяя их, предполагая, что имена файлов являются символами новой строки, отделенными внутри файла file_list:
while IFS= read -r f; do
find /path1 -name "$f" -exec cp '{}' /path2 \;
done <file_list
Или, если вы знаете, что файлы будут находиться в каталоге /path1, а не в любой подкаталог из него, вы можете просто использовать массив для получения имен файлов и использовать (GNU) cp напрямую, опять же предполагая, что имена файлов, разделенных символом новой строки, внутри file_list:
(
IFS=$'\n'
files=( $(<file_list) )
cp -t /path2 "${files[@]}"
)
В случае огромного количество файлов, вам лучше было бы перебирать и cp индивидуально, а не вбрасывать в массив:
while IFS= read -r f; do cp -- "$f" /path2; done <file_list
Если у вас есть список файлов, например, например, file1 file2 file3 ... непосредственно в конструкции for, то просто используя двойные кавычки:
for f in file1 file2 file3; do
find /path1 -name "$f" -exec cp '{}' /path2 \;
done
Теперь вы можете использовать cp прямо здесь, если все файлы не будут находиться в каком-либо подкаталоге:
cp -t /path2 file1 file2 file3
Или вы можете указать статические абсолютные или относительные пути, если вы хотите использовать cp только для работы с любыми файлами под любым подкаталогом.
Вы пояснили, что ваш список файлов не является текстовым файлом, а серией имен файлов, которые вы хотите найти с помощью find. Вы сказали, что это работает для вас:
for f in file1 file2 file3 ... ; do find <path> -name "$f" \; done;
Предположительно, вы имеете в виду, что вы получаете ожидаемый список файлов из этой команды.
Тогда вы говорите, что это не работает: [!d2 ]
for f in file1 file2 file3 ... ; do find <path> -name "$f" -exec cp '{}' <path2> \; done
Предположительно, вы имеете в виду, что файлы, перечисленные выше, не копируются в <path2>. Я не уверен, какую ошибку вы получаете, но, как написано, я бы ожидал, что команда будет выглядеть так:
$for f in file1 file2 file3 ... ; do find <path> -name "$f" -exec cp '{}' <path2> \; done
>
, ожидающая отсутствия ;. Предполагая, что вы исправили эту проблему, я не могу думать ни о какой другой очевидной причине, почему ваша команда определенно потерпит неудачу. Вы должны иметь возможность управлять с помощью
for f in file1 file2 file3 ... ; do find <path> -name "$f" -exec cp '{}' <path2> \; ; done
Однако я предлагаю использовать флаг -t для указания каталога. Я должен поблагодарить Дэвида Фёрстера за этот комментарий, который, как мне кажется, показывает лучшую форму для вызова cp или mv, используя find.
for f in file1 file2 file3; do find /path/to/search -name "$f" -exec cp -vt /path/to/destination -- {} + ; done
for var in things; do command "$var"; done
Если она не видит ; до done, bash будет ожидать ; или сигнал прерывания. Два вопроса:
for f in some_file не перебирает содержимое some_file, просто перебирает или принимает литералную строку some_file. Чтобы преодолеть это, забудьте про цикл for для итерации содержимого файла, используйте правильно реализованную конструкцию while. В этом случае переменные не будут расширены при вставке в одинарные кавычки '$f'. Чтобы ваша оригинальная идея работала, используйте двойные кавычки.Объединяя их, предполагая, что имена файлов являются символами новой строки, отделенными внутри файла file_list:
while IFS= read -r f; do
find /path1 -name "$f" -exec cp '{}' /path2 \;
done <file_list
Или, если вы знаете, что файлы будут находиться в каталоге /path1, а не в любой подкаталог из него, вы можете просто использовать массив для получения имен файлов и использовать (GNU) cp напрямую, опять же предполагая, что имена файлов, разделенных символом новой строки, внутри file_list:
(
IFS=$'\n'
files=( $(<file_list) )
cp -t /path2 "${files[@]}"
)
В случае огромного количество файлов, вам лучше было бы перебирать и cp индивидуально, а не вбрасывать в массив:
while IFS= read -r f; do cp -- "$f" /path2; done <file_list
Если у вас есть список файлов, например, например, file1 file2 file3 ... непосредственно в конструкции for, то просто используя двойные кавычки:
for f in file1 file2 file3; do
find /path1 -name "$f" -exec cp '{}' /path2 \;
done
Теперь вы можете использовать cp прямо здесь, если все файлы не будут находиться в каком-либо подкаталоге:
cp -t /path2 file1 file2 file3
Или вы можете указать статические абсолютные или относительные пути, если вы хотите использовать cp только для работы с любыми файлами под любым подкаталогом.
Вы пояснили, что ваш список файлов не является текстовым файлом, а серией имен файлов, которые вы хотите найти с помощью find. Вы сказали, что это работает для вас:
for f in file1 file2 file3 ... ; do find <path> -name "$f" \; done;
Предположительно, вы имеете в виду, что вы получаете ожидаемый список файлов из этой команды.
Тогда вы говорите, что это не работает:
for f in file1 file2 file3 ... ; do find <path> -name "$f" -exec cp '{}' <path2> \; done
Предположительно, вы имеете в виду, что файлы, перечисленные выше, не копируются в <path2>. Я не уверен, какую ошибку вы получаете, но, как написано, я бы ожидал, что команда будет выглядеть так:
$for f in file1 file2 file3 ... ; do find <path> -name "$f" -exec cp '{}' <path2> \; done
>
, ожидающая отсутствия ;. Предполагая, что вы исправили эту проблему, я не могу думать ни о какой другой очевидной причине, почему ваша команда определенно потерпит неудачу. Вы должны иметь возможность управлять с помощью
for f in file1 file2 file3 ... ; do find <path> -name "$f" -exec cp '{}' <path2> \; ; done
Однако я предлагаю использовать флаг -t для указания каталога. Я должен поблагодарить Дэвида Фёрстера за этот комментарий, который, как мне кажется, показывает лучшую форму для вызова cp или mv, используя find.
for f in file1 file2 file3; do find /path/to/search -name "$f" -exec cp -vt /path/to/destination -- {} + ; done
for var in things; do command "$var"; done
Если она не видит ; до done, bash будет ожидать ; или сигнал прерывания. Два вопроса:
for f in some_file не перебирает содержимое some_file, просто перебирает или принимает литералную строку some_file. Чтобы преодолеть это, забудьте про цикл for для итерации содержимого файла, используйте правильно реализованную конструкцию while. В этом случае переменные не будут расширены при вставке в одинарные кавычки '$f'. Чтобы ваша оригинальная идея работала, используйте двойные кавычки.Объединяя их, предполагая, что имена файлов являются символами новой строки, отделенными внутри файла file_list:
while IFS= read -r f; do
find /path1 -name "$f" -exec cp '{}' /path2 \;
done <file_list
Или, если вы знаете, что файлы будут находиться в каталоге /path1, а не в любой подкаталог из него, вы можете просто использовать массив для получения имен файлов и использовать (GNU) cp напрямую, опять же предполагая, что имена файлов, разделенных символом новой строки, внутри file_list:
(
IFS=$'\n'
files=( $(<file_list) )
cp -t /path2 "${files[@]}"
)
В случае огромного количество файлов, вам лучше было бы перебирать и cp индивидуально, а не вбрасывать в массив:
while IFS= read -r f; do cp -- "$f" /path2; done <file_list
Если у вас есть список файлов, например, например, file1 file2 file3 ... непосредственно в конструкции for, то просто используя двойные кавычки:
for f in file1 file2 file3; do
find /path1 -name "$f" -exec cp '{}' /path2 \;
done
Теперь вы можете использовать cp прямо здесь, если все файлы не будут находиться в каком-либо подкаталоге:
cp -t /path2 file1 file2 file3
Или вы можете указать статические абсолютные или относительные пути, если вы хотите использовать cp только для работы с любыми файлами под любым подкаталогом.
Вы пояснили, что ваш список файлов не является текстовым файлом, а серией имен файлов, которые вы хотите найти с помощью find. Вы сказали, что это работает для вас:
for f in file1 file2 file3 ... ; do find <path> -name "$f" \; done;
Предположительно, вы имеете в виду, что вы получаете ожидаемый список файлов из этой команды.
Тогда вы говорите, что это не работает:
for f in file1 file2 file3 ... ; do find <path> -name "$f" -exec cp '{}' <path2> \; done
Предположительно, вы имеете в виду, что файлы, перечисленные выше, не копируются в <path2>. Я не уверен, какую ошибку вы получаете, но, как написано, я бы ожидал, что команда будет выглядеть так:
$for f in file1 file2 file3 ... ; do find <path> -name "$f" -exec cp '{}' <path2> \; done
>
, ожидающая отсутствия ;. Предполагая, что вы исправили эту проблему, я не могу думать ни о какой другой очевидной причине, почему ваша команда определенно потерпит неудачу. Вы должны иметь возможность управлять с помощью
for f in file1 file2 file3 ... ; do find <path> -name "$f" -exec cp '{}' <path2> \; ; done
Однако я предлагаю использовать флаг -t для указания каталога. Я должен поблагодарить Дэвида Фёрстера за этот комментарий, который, как мне кажется, показывает лучшую форму для вызова cp или mv, используя find.
for f in file1 file2 file3; do find /path/to/search -name "$f" -exec cp -vt /path/to/destination -- {} + ; done
for var in things; do command "$var"; done
Если она не видит ; до done, bash будет ожидать ; или сигнал прерывания.