Isso deve ser feito, supondo que nenhum dos seus nomes de arquivo contenha < :
grep -Po 'file://\K[^<]+' now.xspf | while IFS= read -r file; do
song="${file//%20/ }"
cp "$song" /path/to/target;
done
Explicação
O -o sinalizador diz grep para imprimir apenas a parte correspondente de uma linha e o -P ativa Perl Compatible Regular Expressions que nos fornecem \K . O \K faz com que grep descarte tudo que foi correspondido antes, portanto, o resultado é que file://\K[^<]+ corresponderá à string mais longa de caracteres que não são < e que vêm após file:// .
Isso é então enviado para um loop while com IFS= para desativar a divisão de palavras no espaço em branco e -r para tratar as barras invertidas como caracteres normais.
A linha SONG="${file//%20/ }" converte todos os %20 em (espaço vazio). Caso contrário, ele retornará um erro de "caminho não encontrado" quando tentar copiar as músicas na última linha.
Se os nomes dos seus arquivos puderem conter < , use isso:
perl -lne '@foo=(m#file://(.+?)</location>#g); END{print $_ for @foo}' playlist.xspf |
while IFS= read -r file; do
cp "$file" /path/to/target/;
done
Observação: As duas soluções assumem que nenhum dos seus nomes de arquivo contém novas linhas.