Как мне найти и скопировать произвольный список файлов в терминале?
Я сейчас пытаюсь найти файлы в терминале. Я нашел много ответов о том, как найти файлы по определенному шаблону, например, файлы, заканчивающиеся определенным расширением или с определенного периода времени и т. Д.
Однако я пытался выяснить, как найти и скопировать несколько произвольных файлов, например, список определенных файлов.
Скажем, например, у меня есть следующие папки и файлы
/Documents/Folder_1/
с содержанием:
file1.txt file2.txt file3.txt file4.txt
/Documents/Folder_2/
с содержанием:
file5.txt file6.txt file7.txt
/Documents/Folder_3/
с содержанием:
file8.txt, file9.txt
и что я хочу сделать, это сделать новую папку под названием Folder_4
и скопировать file2.txt
, file5.txt
а также file9.txt
в эту новую папку.
Есть ли способ сделать это через терминал?
Я попытался сделать текстовый список имен файлов и назвать его чем-то вроде list.txt, например
file2.txt
file5.txt
file9.txt
и загружаем этот текстовый файл в команду Find, например, так:
find /home/usr/Documents -name $cat list.txt
с мыслью, что я затем передам вывод либо текстовому списку, либо команде копирования, но, похоже, это не сработает.
Мне интересно, есть ли способ найти несколько произвольных файлов с помощью команды Find, или я думаю об этом неправильно?
РЕДАКТИРОВАТЬ: Просто хотел сказать спасибо за ответы и резюмировать, как я закончил подход к этой проблеме. Я особенно ценю то, что кто-то объяснил мне, что команда find не предназначена для приема более одного ввода без флага -o, это заставило меня чувствовать себя немного менее глупо из-за того, что я не смог понять это!
В итоге я сделал небольшой сценарий оболочки и запросил ввод данных пользователем, использовал массив и цикл for для итерации по списку.
В основном сценарий, который я закончил, более или менее похож на это:
#!/bin/bash
#take user input
echo "Please enter list of filenames to search"
read list
echo "Please enter the folder path to search"
read path
echo "Please enter the destination folder path"
read destination
#use inputted list as input for array, and then use a for loop to find each item on list one by one
array=($list)
for i in "${array[@]}"
do
find "$path" -iname "i" -exec cp '{}' "$destination" \;
done
3 ответа
К сожалению find
командования -name
Предикат принимает только один шаблон. Если вы хотите искать несколько файлов по имени, вам нужно связать их -o
(логическое OR
) оператор - что-то вроде:
find Documents/ \( -name "file2.txt" -o -name "file5.txt" -o -name "file9.txt" \) -print
Это затрудняет построение поиска программно из списка; самое близкое, что я могу прийти к вашей попытке команды:
читать список в массив оболочки
mapfile -t files < list.txt
использовать оболочку Bash
printf
построить список предикатовprintf -- '-name "%s" -o ' "${files[@]}"
использование
eval
оценить полученную командную строку
Здесь есть морщина, поскольку мы используем printf
возможность повторного использования формата для построения списка таким образом, у нас остается "висячий" -o
; мы можем обойти это, завершив список с -false
тест (с -o -false
является логическим не-опцией), так что наша последняя строка предиката становится
"\( $(printf -- '-name "%s" -o ' "${files[@]}") -false \)"
Собираем все вместе - дано
$ tree dir
dir
├── Folder_1
│ ├── file1.txt
│ ├── file2.txt
│ ├── file3.txt
│ └── file4.txt
├── Folder_2
│ ├── file5.txt
│ ├── file6.txt
│ └── file7.txt
└── Folder_3
├── file8.txt
└── file9.txt
3 directories, 9 files
а также
$ cat list.txt
file2.txt
file5.txt
file9.txt
затем
$ mapfile -t files < list.txt
$ eval find dir/ "\( $(printf -- '-name "%s" -o ' "${files[@]}") -false \)" -print
dir/Folder_1/file2.txt
dir/Folder_2/file5.txt
dir/Folder_3/file9.txt
Чтобы скопировать файлы, а не просто перечислить их, вы можете сделать
$ mkdir newdir
$ eval find dir/ "\( $(printf -- '-name "%s" -o ' "${files[@]}") -false \)" -exec cp -t newdir/ -- {} +
в результате чего
$ tree newdir
newdir
├── file2.txt
├── file5.txt
└── file9.txt
0 directories, 3 files
Обратите внимание eval
Команда сильна и потенциально открыта для злоупотреблений: используйте с осторожностью.
На практике, учитывая, что вы, похоже, хотите найти только небольшое количество файлов, подход KISS заключается в том, чтобы принять снижение производительности нескольких find
звонки и просто использовать цикл:
while read -r f; do
find dir/ -name "$f" -exec cp -v -- {} newdir/ \;
done < list.txt
или даже используя xargs
xargs -a list.txt -n1 -I@ find dir/ -name @ -exec cp -v -- {} newdir/ \;
Все еще пытаюсь выяснить ваш вопрос самым ясным способом.
Вы пытаетесь найти какие-то конкретные файлы в каталоге? Если да и существует шаблон, который вы можете использовать регулярные выражения, чтобы найти их, используйте find <directory> -regex "your_regex"
и если нет шаблона, вам нужно указать имена файлов, которые вы ищете. Вы можете использовать скрипт Python ниже, чтобы скопировать ваши файлы.
import shutil
import os
list = [file1.txt, file2.txt] # replace with your file list.
destination = "your_destination" # replace with your destination.
directory = os.getcwd() # replace os.getcwd() with your directory.
for dirpath, dirname, filenames in os.walk(directory, topdown='true'):
for file in filenames :
if file in list :
shutil.copy(os.path.join(dirpath, file), destination)
Если вышеупомянутое не ваше дело, тогда я предлагаю использовать это:
find <somewhere> -name "something" | xargs cp -t <your_destination>
Сценарий bash ниже, если он выполняется из каталога верхнего уровня (скажем, из вашего ~/Documents
папка), создаст новую директорию и скопирует / переместит нужные файлы. По умолчанию он отображает только найденные файлы, так что пользователь может проверить, является ли это желаемым результатом. Примечание: заменить echo
с cp
скопировать или mv
двигаться.
Список файлов назначен regex
переменная с \|
разделитель (который для grep
значит логично или утверждение)
copy_files.sh
:
#!/bin/bash
new_dir="./dir4"
[ -d "$new_dir" ] || mkdir "$new_dir"
regex="file1\|file3\|file6"
find . -print0 | while IFS= read -d $'\0' line;
do
if grep -q "$regex" <<< $line
then
filename=$(basename "$line")
printf "%s\n" "Found $filename"
echo "$line" "$new_dir"/"$filename"
# Replace echo with cp for copying,
# or mv for moving
fi
done
bash-4.3$ ./copy_files.sh
Found file6
./dir3/file6 ./dir4/file6
Found file3
./dir1/file3 ./dir4/file3
Found file1
./dir1/file1 ./dir4/file1
Как вы можете видеть выше, скрипт сообщает, какой файл был найден и куда он скопирует или переместит, например, в последней строке он скопирует / переместит ./dir1/file1
в ./dir4/file1
если это было на самом деле с помощью cp
или же mv
и не только echo