O principal problema aqui é evitar mover os arquivos mais de uma vez. Um loop ingênuo corre o risco de encontrar dir/x.Item
, movê-lo para dir/dir1/dir2/x.Item
, e então encontrar dir/dir1/dir2/x.Item
e movê-lo para dir/dir1/dir2/dir1/dir2/x.Item
, e então ...
Embora eu não conseguisse fazer o Gnu find
fazer isso em alguns minutos de teste, eu noto que o Posix diz "Se um arquivo é removido ou adicionado à hierarquia de diretórios que está sendo pesquisada, não é especificado se ou não find
inclui esse arquivo em sua pesquisa. " Então, um deve ser defensivo.
Portanto, a solução mais fácil é primeiro construir a lista de arquivos e depois processar a lista.
Se nenhum arquivo .Item
tiver um caractere de nova linha em seu nome, será fácil usar find
para construir a lista. Supondo que não haja muitos arquivos, isso pode ser feito diretamente em uma matriz bash:
mapfile -t files < <(find . -name '*.Item')
Em seguida, é simples processar os arquivos:
for f in "${files[@]}"; do
# make the target directory if it doesn't yet exist (-p)
mkdir -p "$(dirname "$f")"/dir1/dir2
# move the file
mv "$f" "$(dirname "$f")/dir1/dir2
done
Se houver muitos arquivos, precisamos ser mais eficientes e menos arrogantes sobre a memória. Podemos construir uma lista de diretórios em vez de uma lista de arquivos ( sort -u
elimina duplicatas):
mapfile -t dirs < <(find . -name '*.Item' -printf %h | sort -u)
Agora podemos processar os diretórios:
for d in "${dirs[@]}"; do
mkdir -p "$d/dir1/dir2"
mv "$d/*.Item" "$d/dir1/dir2"
done
Por fim, se houver muitos diretórios, poderemos usar um arquivo temporário em vez de um array:
tmp=$(mktemp)
find . -name '*.Item' -printf %h | sort -u > "$tmp"
while IFS= read -r dir; do
mkdir -p "$d/dir1/dir2"
mv "$d/*.Item" "$d/dir1/dir2"
done < "$tmp"
rm "$tmp"