Пожалуйста, кто-нибудь может подсказать мне, как это сделать?
У меня есть два списка (оба с sha1sums и их относительными именами файлов), но они отформатированы по-разному, вот пример:
список01.txt
artist'ssomesong.mp3,3f1dfd39e88e00477483dfd578d5284f5490a0a5
hello(previous one).sh,55a5fdde4843fc2f9d9e691cb658b6389d698b22
mymovie [1989, director's cut].mov,4bdee0fc0eb7a3dbc5bbe2b65a02a1f9dc76c443
[etc...]
список02.txt
3f1dfd39e88e00477483dfd578d5284f5490a0a5 /path/to/my new music/album.wav
f77921adf6748f65fe688a5484ed901d4g9932hh /path/to/movies/[YEAR]/mymovie [1989, director's cut].mov
55a5fdde4843fc2f9d9e691cb658b6389d698b22 /path/to/scripts,regexs/hello(previous one).sh
[etc...]
Как вы можете видеть, единственная хорошая запись — для sha1sum 55a5fdde4843fc2f9d9e691cb658b6389d698b22
с именем файла hello(previous one).sh
(2-я строка в list01.txt
и 3-я строка в list02.txt
).
Имена файлов и пути могут содержать пробелы и специальные символы (например: ' " [ ] ( ) { } и так далее...).
Единственное, в чем нужно быть на 100% уверенным, это то, что list01.txt
всегда форматируется как ,sha1sum
; и list02.txt
всегда будет иметь sha1sum /
(два пробела перед /).
Как и в названии этого вопроса, я хотел бы использоватьесли-условиев скрипте bash, который проверяет оба списка на предмет совпадений (TRUE — еслии sha1sum и имя файла одинаковы) и когда он их найдет, то скопирует каждое вхождение, используя
cp $source $destination
source=reads the /path/to/filename from list02.txt
destination=/wherever/i/want/
Спасибо!
решение1
Предположения:
- Присутствуют инструменты GNU (нестандартные
xargs
иcp
опциональные, возможно неработающий разделитель NUL в другихawk
') - Длина хеша всегда составляет 40 символов.
- Всегда есть два пробела, разделяющие хэш и путь к файлу.
list02.txt
- В обоих файлах отсутствуют символы вертикальной
|
черты (в противном случае используйте другой разделитель)
Первый шаг — объединить оба файла:
join -t'|' -1 2 \
<(sed -E 's/,(.{40})$/|\1/' list01.txt | sort -t'|' -k2) \
<(sed -E 's/(.{40}) /\1|/' list02.txt | sort -t'|' -k1)
- Первый файл: заменить разделитель
,
на|
и отсортировать файл по второму полю - Второй файл: заменить разделитель
(два пробела) на
|
и отсортировать по первому полю - Объединить файлы в хэш-поле
Выход:
3f1dfd39e88e00477483dfd578d5284f5490a0a5|artist'ssomesong.mp3|/path/to/my new music/album.wav
55a5fdde4843fc2f9d9e691cb658b6389d698b22|hello(previous one).sh|/path/to/scripts,regexs/hello(previous one).sh
Затем используйте awk
для проверки, присутствует ли имя файла field2 как имя файла в последнем поле. Если это так, выведите последнее поле с разделителем NUL и передайте результат в конвейер для xargs
копирования файлов в целевой каталог.
join -t'|' -1 2 \
<(sed -E 's/,(.{40})$/|\1/' list01.txt | sort -t'|' -k2) \
<(sed -E 's/(.{40}) /\1|/' list02.txt | sort -t'|' -k1) \
| awk -F '|' '
{
fname1=$2; sub(/.*\//, "", fname1) # extract filename1
fname2=$3; sub(/.*\//, "", fname2) # extract filename2
}
fname1 == fname2{ printf $3 "\0" } # compare filenames, print filepath with NUL separator
' | xargs -r0 cp -n -t /path/to/destination
Параметры копирования:
-n
не перезаписывать существующие файлы-t
целевой каталог
Как сценарий:
#!/bin/bash
join -t'|' -1 2 \
<(sed -E 's/,(.{40})$/|\1/' "$1" | sort -t'|' -k2) \
<(sed -E 's/(.{40}) /\1|/' "$2" | sort -t'|' -k1) \
| awk -F '|' '
{
fname1=$2; sub(/.*\//, "", fname1) # extract filename1
fname2=$3; sub(/.*\//, "", fname2) # extract filename2
}
fname1 == fname2{ printf $3 "\0" } # compare filenames, print filepath with NUL separator
' | xargs -r0 cp -n -t "$3"
Запустите его как:
./script.sh list1 list2 /path/to/destination