Mover arquivos com "e * causou erro

Aug 22 2020

Estou tendo problemas para mover arquivos em um script bash. Tenho tentado diferentes soluções que encontrei aqui, sobre o mesmo problema, mas não consigo encontrar nada que funcione ..

minha última tentativa foi adicionar, shopt -s dotglob nullglobmas não resolveu nada.

Neste teste,

jdir0="/media/sf_Mediaserver3/test22/abbamax.(6th.copy)..kansas.(1999)"


mv -v "$jdir0/*" "$jdir0/subs/" &>> $debuglog

.. e eu recebo:

mv: cannot stat '/media/sf_Mediaserver3/test22/abbamax.(6th.copy)..kansas.(1999)/*': No such file or directory

mas, sim, existem!

drwxrwx--- 1 root vboxsf   4096 Aug 22 07:06  ../
-rwxrwx--- 1 root vboxsf      0 Aug 21 17:19 'kallee.(222)..nnn.srt'*
-rwxrwx--- 1 root vboxsf 159363 Aug 21 17:26 'movie.test(2929).ismim.mp4'*
drwxrwx--- 1 root vboxsf      0 Aug 22 07:06  subs/

(a razão pela qual os nomes são realmente estranhos é que antes desta função estou testando para remover caracteres inválidos)

atualização: Aparentemente, recebi erros intermitentes e, finalmente, após dias, rastreei o problema do servidor (onde os arquivos estavam armazenados). Aparentemente, esses erros ocorriam se o servidor não tivesse concluído a alteração de salvar / nome e o script solicitasse algo novo. Por exemplo, renomear o arquivo A para B e depois pedir para renomear B para C ANTES de o servidor ter executado a primeira solicitação, o que resultou no servidor dizer: B não existe, o que obviamente causou um código de erro.

Respostas

7 StéphaneChazelas Aug 22 2020 at 05:44

*é um operador glob do shell. Ele precisa ser deixado sem citação para ser reconhecido como tal. Quando citado, /media/sf_Mediaserver3/test22/abbamax.(6th.copy)..kansas.(1999)/*é passado literalmente para mve mvtenta mover aquele arquivo chamado *, e esse arquivo não existe.

Então você precisa:

mv -v -- "$jdir0"/* "$jdir0/subs/" >> "$debuglog" 2>&1

Para que o shell se expanda "$jdir0"/*na lista de arquivos correspondentes antes de chamar mv.

Você não quer nullglobaqui como isso significaria que, na ausência de arquivos correspondentes que "$jdir0"/*teste padrão, mvseria invocado com apenas -v, --e media/sf_Mediaserver3/test22/abbamax.(6th.copy)..kansas.(1999)/subs/causando um erro de sintaxe confundindo por mv.

failglobabortar o comando quando os globs não correspondem pode ser uma opção melhor nesse caso, embora note que bashaborta de maneiras inconsistentes nesse caso, dependendo do contexto em que o comando é invocado, o que torna essa opção difícil de usar em scripts.

dotglob é permitir que globs correspondam a arquivos ocultos.

Agora, observe que os globs correspondem aos arquivos independentemente de seu tipo¹, de modo que o item *acima também corresponderá subs. Se subsfor um link simbólico para um diretório, mvfelizmente moveremos esse subslink simbólico para esse diretório, fazendo com que todas as movimentações subsequentes falhem, pois o subsdiretório de destino agora se foi. Se subsfor um subdiretório simples, mvprovavelmente reclamará que não pode mover um diretório para dentro dele.

Então você pode querer escrevê-lo:

shopt -s extglob
mv -v -- "$jdir0"/!(subs) "$jdir0/subs/" >> "$debuglog" 2>&1

Onde !(pattern)está o operador ksh extended glob que corresponde a qualquer nome de arquivo que não corresponda pattern, então mova qualquer arquivo mas subs.

Observe também que, no bashshell, as expansões de parâmetro também precisam ser citadas quando em alvos de redirecionamentos, mesmo em instâncias de shell não interativas (exceto quando bash está no modo POSIX).


¹ a menos que você use zsh em vez de bash e seus qualificadores glob, como *(.)para mover apenas arquivos regulares