Como impedir que o `mv` mova uma coleção de arquivos para uma única regular?

17

Acabei de perder uma pequena parte da minha coleção de áudio, por um erro estúpido que cometi. :-(
Felizmente, eu tinha um backup relativamente recente, mas ainda era irritante. Além do seu, o outro culpado foi o que causou a travessura mv, que mostrará o seguinte:

Os arquivos de áudio tinham um certo esquema:

ARTIST - Some Title YY.mp3

onde YYé a especificação do ano com 2 dígitos.

mkdir 90<invisible control character>

(Até o momento, eu não sabia que havia digitado um terceiro caractere em excesso que era invisível ...!)
Em vez de ter tudo em um diretório, eu queria ter todas as músicas dos anos 90 em um diretório. Então eu digitei:

find . -name '* 9?.mp3' -exec mv {} 90 \;

Não é tão difícil entender o que aconteceu, não é? : ->
O resultado (desastroso) foi um diretório vazio virgem chamado '90 something '(com algo sendo o caractere de controle "invisível") e um único arquivo chamado' 90 ', sobrescrito n vezes.

Todos os arquivos foram embora. :-(( (obviamente)

Desejo mvteria verificado no tempo se a assinatura do "arquivo" de destino (lembre-se em * NIX: Tudo é um arquivo ) começa com um d------(por exemplo drwxr-xr-x). E, é claro, se o destino existe . Existe uma variante do cenário acima mencionado, quando você simplesmente esqueceu-se para mkdiro diretório em primeiro lugar. (mas é claro, você assumiu que está lá ...)

Mesmo o nosso OS pet-ódio começando com a capital W faz isso. Você é solicitado a especificar o tipo de destino (arquivo? Diretório?), Se solicitar.

Por isso, estou me perguntando se nós * NIXers ainda temos que escrever um " mvscriptlet" para evitar esse tipo de surpresa indesejável.

erro de sintaxe
fonte
2
Nem todos os arquivos foram removidos. Pelo menos um .mp3deve estar lá com o nome 90, poderia ter sido aquele para o qual você não tinha um backup.
Anthon
2
Heh, você tem um senso de humor cínico, está doido! :-P Bem, esse foi o arquivo chamado "um único arquivo" em negrito no meu OP. :)
syntaxerror 21/11
2
mvnão é o problema aqui, tecnicamente, ele não sabe que você está movendo uma série de arquivos. Você está executando mvuma vez para cada arquivo. É assim que find -exec ;funciona. Se você tivesse usado find -exec +(como em alguns dos comentários) mv teria gritado assim que recebesse mais de um argumento.
Etan Reisner
Embora a execução mvde cada arquivo possa parecer um pouco menos pensada a princípio, ela (como eu havia dito anteriormente) será a única solução sã quando os arquivos de origem estiverem espalhados entre vários subdiretórios. Que no meu caso de teste, os arquivos de origem estavam todos em um diretório, não significa que esse seja o meu caso de teste real . Na verdade, é apenas uma simplificação, porque eu posso elaborar isso sozinho depois. Além disso, torna as perguntas menos demoradas para ler devido ao tamanho reduzido. :)
syntaxerror 23/11
Por que você espera mvexigir que o destino exista? mv oldfile newfileé a maneira de renomear um arquivo, e é bobagem esperar newfilejá existir e ser um diretório.
Barmar

Respostas:

37

Você pode anexar /a ao destino se desejar mover arquivos para um diretório. Caso o diretório não exista, você receberá um erro:

mv somefile somedir/
mv: cannot move ‘somefile’ to ‘somedir/’: Not a directory

Caso o diretório exista, ele move o arquivo para esse diretório.

Marco
fonte
4
Muito obrigado! Esse deveria ser o meu futuro salva-vidas então. Eu devo-te uma. (Assim como uma nota, um amigo meu tinha feito o mesmo erro que alguns anos atrás, então eu sinto que não estou sozinho.)
SyntaxError
1
Além disso, ao usar determinados shells, você tem a opção Tab para preencher automaticamente os nomes de arquivos para você. Se não consigo lembrar o nome do diretório e não quero uma bagunça como a que você teve recentemente, basta digitar um caractere ou dois do nome do diretório e a tecla HIT TAB . Então você pode ter certeza, que ela existe, porque auto-complete colocá-lo lá ....
Andyz Smith
@AndyzSmith Bem, é exatamente isso. Você pode chamar de hábito meu usar sempre TAB para diretórios ou caminhos complicados , mas não para os de duas letras. :) Mas pense bem ... talvez eu deva considerar o último caso também de agora em diante.
Syntaxerror 12/12/14
20

O coreutils GNU mvjá tem uma opção especificando que você deseja mover para um diretório: -t/ --target-directory. Se o argumento para essa opção não existir, mvele reclamará em vez de mover todos os seus arquivos para o mesmo nome de arquivo.

Eu teria escrito o seu motor da seguinte maneira:

find . -name '* 9?.mp3' -exec mv -t 90 {} +

Observe o uso de em +vez de \;agrupar o máximo de nomes de arquivos possível, resultando em uma execução mais rápida.

Anthon
fonte
Obrigado. (Esperando que isso não é um daqueles GNU-ismos novamente, embora.)
SyntaxError
2
@erro de sintaxe. É um GNUism. POSIXly:find . -name '* 9?.mp3' -exec sh -c 'exec mv "$@" 90/' sh {} +
Stéphane Chazelas
Muito obrigado por este one-liner mais SNEAKY! Apenas pense que foi um +5 que eu lhe dei. :) Tornará desnecessárias muitas tentativas de tentativa e erro .--- E você sabe muito bem por que fiz essa observação. Só preciso estar em uma máquina que seja diretamente POSIX (não acontece muito raramente), e eu vou ter o próximo problema aqui. :)
syntaxerror 21/11
1
@syntaxerror Se esta resposta resolver seu problema, aguarde um minuto e clique na marca de seleção abaixo da contagem de votos à esquerda, isso significa para todos que seu problema foi resolvido e é a maneira como agradecemos no site. Vi que apenas algumas de suas outras perguntas respondidas aceitaram respostas, talvez você as deseje também.
Anthon
Não, é simplesmente um propósito. Frequentemente, eu esperava várias semanas ou às vezes 2 meses até aceitar uma resposta. O motivo é que algumas pessoas muito conhecedoras têm uma agenda MUITO carregada e só podem encontrar tempo para dar sua (geralmente a melhor) resposta após algumas semanas. Por isso, sempre acho respeitoso esperar que eles parem. Bem, e se eles realmente não o fizerem, não hesitarei em acertar a marca de seleção, com certeza. Além disso, não vejo por que algumas pessoas estão sempre com tanta pressa em SE + seus sabores. Fácil, pessoal. Não pule a arma. :) Este não é seu chefe pressionando você.
Syntaxerror 21/11
10

Além disso, se você geralmente planeja evitar substituições acidentais no futuro, existe a -iopção mv. Eu pessoalmente não consigo pensar em nenhuma desvantagem se você

alias mv='mv -i'

Se você precisar sobrescrever algo, simplesmente passe a -fopção.

Os aliases só entram em vigor se você digitar o comando diretamente em um shell interativo, não para casos como invocação por find. Você poderia ter corrido

find . -name '* 9?.mp3' -exec mv -i {} 90 \;

e você seria solicitado se mvtentasse sobrescrever um arquivo existente.

ayekat
fonte
4
Ou - como você provavelmente não sabia - digite ´ \ mv `em vez de mv. Esse "truque" menos conhecido fará com que o comando com a barra invertida anexada a ele ignore quaisquer definições de alias.
Syntaxerror 21/11
Obviamente, se você está realmente falando find ... -exec mv ..., precisará criar ~/bin/mv(ou algum outro diretório apropriado) e fazê-lo /bin/mv -i "$@"- porque find ... -execnão procura aliases.
G-Man diz 'Reinstate Monica'
Nesse caso, eu preferiria env mv. Menos digitação. :) Como meu layout de teclado local exige que a tecla SHIFT seja pressionada para uma barra, sempre eu preferiria versões "sem barra" (se aplicável).
Syntaxerror # 22/14
1
@ syntaxerror: BTW, quando você responde a um comentário (em um novo comentário), é convencional mencionar o nome do autor, precedido por "@", como em "@ G-Man". Dessa forma, eu sou notificado. (Consegui responder ao seu último comentário de maneira semi-oportuna porque fui notificado pelo comentário de Jenny D.) Você pode abreviar ou usar um nome inteiro (sem espaços), por exemplo, “@ StéphaneChazelas”. O autor de uma postagem é notificado automaticamente dos comentários a essa postagem. Consulte os parágrafos de resposta nos comentários desta página de ajuda .
G-Man Diz 'Reinstate Monica'
1
Sinto muito, fiquei sem Internet por um dia. @ G-Man Concordo que ter uma versão pessoal de mvum local no início de $PATHseria uma solução mais limpa. Por outro lado, eu mvdescuidadamente no shell interativo (porque isso acontece rápido). No momento em que componho algo mais complexo, como findum forloop ou mesmo um script de shell, costumo executar algumas execuções a seco (usando echo) para garantir que não quebre algo. Nesses casos, não preciso de mãos dadas mv, porque já estou pensando nisso.
ayekat
7

Além das excelentes respostas acima, gostaria de esclarecer por que você não recebeu a pergunta sobre mover ou não os arquivos.

Se você mover um arquivo para um novo nome, e esse nome não for um diretório, mvrenomeará seu arquivo para o novo nome.

O problema aqui é que você estava usando findpara executar mvuma vez por arquivo , não uma vez para todos os arquivos .

Se, em vez disso, você tiver feito mv *90.mp3 90, mvteria falhado com a mensagem de erro "O arquivo de destino não é um diretório".

Outro conselho é usar o preenchimento de guias ao digitar o caminho de destino. Ele mostrará se o destino é um diretório adicionando /ao nome do destino. Você também pode mv -iser perguntado se deseja substituir um arquivo existente.

Jenny D
fonte
Se, em vez disso, você tiver feito mv *90.mp3 90, o mv teria falhado com a mensagem de erro "O arquivo de destino não é um diretório". Hah, sim, por que tão complicado, eh? Eu uso sua linha e serei feliz. Apenas neste caso trivial, no entanto. :) Como essa é a maneira normal de fazer minhas perguntas: eu as reduzirei por uma questão de simplicidade. Ninguém se opôs findaté agora, considerando que os arquivos dos anos 90 também poderiam estar espalhados em vários subdiretórios que eu também quero "capturar". Se e somente se eles estiverem sempre em um diretório de origem, sua mvlinha será aplicável.
Syntaxerror
@ syntaxerror Muito verdadeiro - eu quis dizer isso como um exemplo de como mvse comporta, não como uma crítica à sua escolha de ferramentas.
Jenny D
1
Você provavelmente poderia construir algo combinando finde mv, por exemplo find /music -type d -exec mv {}/*90.mp3 targetdir\;-, mas agora eu me sinto um pouco como eu estou complicar-lo, e simplesmente usando -iou -té mais eficiente
Jenny D
1
@ Jenny: Se o seu find . -type d -exec mv {}/*9?.mp3 target \;exemplo funcionasse, ainda haveria o risco de o mvcomando parecer mv file targetpara cada diretório que continha apenas um *9?.mp3arquivo; então todos esses arquivos (exceto o último) seriam perdidos.
G-Man diz 'Reinstate Monica'
4
@ syntaxerror: Aplaudo seu esforço para expor a parte essencial do seu problema, em vez de todo o script de 42.000 linhas em que ocorre. Mas, mesmo que você queira fazer algo em todos os *.mp3arquivos em uma árvore de diretórios, você poderá shopt -s globstarexecutar o seu comando **/*.mp3- **ele funcionará como a find.
G-Man diz 'Reinstate Monica'
1

Como estratégia alternativa de uso geral, eu gostaria de sugerir transformar esse tipo de operação em um script temporário. Prefiro examinar os resultados finde transformá-los em um mvcomando manualmente, garantindo que eu entenda o que estou fazendo antes de executar. por exemplo.

find . -name '* 9?.mp3' > tmp
vim tmp

Agora eu posso olhar através de uma lista de nomes de arquivos e reescrever o conteúdo do arquivo como um comando shell.

  • Coloque o conteúdo do arquivo em uma linha: ggVGJ
  • Anexar: Imv [esc]
  • Acrescentar: Asomedir/ [esc]
  • Salve o arquivo. Leia isso novamente. Respire.
  • Execute source tmpna linha de comando.

É uma estratégia conservadora, mas muitas vezes fui mordido por comandos -execou erros de digitação ou sedexpansões de shell mal compreendidas, e prefiro adotar uma abordagem lenta e consistente.

Em outras palavras: sou covarde demais para usar -exec.

Tom Rees
fonte
1
Você deixou de fora a :%s/.*/"&"/etapa - porque, nesse caso, você sabe que todo nome de arquivo contém pelo menos um espaço.
G-Man Diz 'Reinstate Monica'
1
Isso o incomodará se os nomes dos arquivos contiverem espaços ou outros caracteres especiais. Você precisa citá-los corretamente. A revisão de uma lista de comandos não é particularmente suscetível de detectar erros. Existem maneiras muito melhores de revisar comandos antes de executá-los, como executar em echo mvvez de mve remover o echose você estiver feliz.
Gilles 'SO- stop be evil'
Spotting um erro como nomes-com-espaços é precisamente o que esta técnica vai ajudar com :-)
Tom Rees
0

Outra opção:

-n, --no-clobber não substitui um arquivo existente

é o mesmo que -i, mas não pergunta, vai falhar.

Jorge Nerín
fonte