Come impedire a `mv` di spostare una raccolta di file in una singola normale?


17

Ho appena perso una piccola parte della mia collezione audio, per uno stupido errore che ho fatto. :-(
GLADLY Ho avuto un backup abbastanza recente, ma era ancora irritante. A parte il tuo, l'altro colpevole che stava facendo il male era mv, che mostrerà come segue:

I file audio avevano un certo schema:

ARTIST - Some Title YY.mp3

dove YYè la specifica dell'anno a 2 cifre.

mkdir 90<invisible control character>

(Fino a questo momento, non sapevo di aver effettivamente digitato un terzo carattere in eccesso che era invisibile ...!)
Invece di avere tutto in una directory, volevo avere tutta la musica degli anni '90 in una directory. Quindi ho scritto:

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

Non è così difficile farsi un'idea di cosa è successo eh? : ->
Il risultato (disastroso) fu una directory vuota vergine chiamata '90 qualcosa '(con qualcosa che era il carattere di controllo "invisibile") e un singolo file chiamato' 90 ', sovrascritto n volte.

TUTTI I FILE SONO STATI ANDATI. : - (((ovviamente)

Wish mvavrebbe verificato in tempo se la firma del "file" di destinazione (ricordare su * NIX: Everything Is A File ) inizia con un d------(es drwxr-xr-x.). E, naturalmente, se la destinazione esiste affatto. C'è una variante dello scenario di cui sopra, quando è sufficiente dimenticato per mkdirla directory prima. (ma ovviamente hai pensato che fosse lì ...)

Anche il nostro sistema operativo che odia gli animali domestici a partire dalla capitale W FA QUESTO. Ti viene persino richiesto di specificare il tipo di destinazione (file? Directory?) Se lo chiedi.

Quindi, mi chiedo se noi * NIXer dobbiamo ancora scrivere uno " mvscriptlet" solo per evitare questo tipo di sorprese più indesiderate.


2
Non tutti i file erano spariti. Almeno uno .mp3dovrebbe essere lì con il nome 90, potrebbe essere stato uno per il quale non hai avuto un backup.
Anthon,

2
Heh, hai un cinico senso dell'umorismo, sei impazzito! :-P Bene, quello era il file chiamato "un singolo file" in grassetto nel mio OP. :)
syntaxerror

2
mvnon è il problema qui, tecnicamente, non sa che stai spostando una serie di file. Stai eseguendo mvuna volta per ogni file. find -exec ;Funziona così . Se avessi usato find -exec +(come in alcuni dei commenti) mv avresti urlato non appena avesse ottenuto più di un argomento.
Etan Reisner,

Sebbene eseguire mvper ogni singolo file possa sembrare inizialmente un po 'meno elaborato, sarà (come ho detto in precedenza) l'unica soluzione sana una volta che i file di origine sono sparsi tra le varie sottodirectory. Che nel mio caso di test, i file di origine fossero tutti in una directory non significa che sia il mio vero caso di test. In realtà è solo una semplificazione, perché potrei facilmente approfondire da solo in seguito. Inoltre, rende le domande meno dispendiose in termini di tempo a causa della loro lunghezza ridotta. :)
syntaxerror

Perché dovresti aspettarti mvdi richiedere l'esistenza della destinazione? mv oldfile newfileè il modo di rinominare un file ed è sciocco aspettarsi newfiledi esistere già ed essere una directory.
Barmar,

Risposte:


37

È possibile aggiungere /a alla destinazione se si desidera spostare i file in una directory. Nel caso in cui la directory non esista riceverai un errore:

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

Nel caso in cui la directory esista, sposta il file in quella directory.


4
Grazie mille! Questo dovrebbe essere il mio futuro salvavita allora. Ti devo un favore. (Proprio come un appunto, un mio amico aveva fatto lo stesso errore alcuni anni fa, quindi sento di non essere solo.)
syntaxerror

1
Inoltre, quando si utilizzano determinati tipi di conchiglie, è disponibile l'opzione Tab per il completamento automatico dei nomi dei file. Se non riesco proprio a ricordare il nome della directory e non voglio un pasticcio come hai fatto di recente, inserisci semplicemente un carattere o due del nome della directory e HIT TAB . Allora puoi essere sicuro che esiste, perché il completamento automatico lo ha messo lì ....
Andyz Smith,

@AndyzSmith Bene, questa è la cosa giusta. Potresti definire mia abitudine usare sempre TAB solo per directory o percorsi complicati , ma non per quelli di 2 lettere. :) Ma vieni a pensarci bene ... forse dovrei davvero considerare anche quest'ultimo caso da ora in poi.
syntaxerror,

20

I coreutils GNU mv già un'opzione che specifica che si desidera spostare in una directory: -t/ --target-directory. Se l'argomento di tale opzione non esiste, mvsi lamenterà invece di spostare tutti i file nello stesso nome file.

Avrei scritto il tuo motore come segue:

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

Nota l'uso +invece di \;, confondendo il maggior numero possibile di nomi di file, con conseguente esecuzione più rapida.


Grazie. (Sperando che non sia di nuovo uno di quei GNU-ismi.)
syntaxerror

2
@Errore di sintassi. È un GNUismo. POSIXly:find . -name '* 9?.mp3' -exec sh -c 'exec mv "$@" 90/' sh {} +
Stéphane Chazelas,

Grazie mille per questo SNEAKY one-liner! Pensa solo a un +5 che ti ho dato. :) Renderà inutili molti tentativi di tentativi ed errori .--- E sai fin troppo bene perché ho fatto quell'osservazione. Ho solo bisogno di essere su una macchina che è chiaramente POSIX (non succede troppo di rado), e avrò il prossimo problema proprio lì. :)
syntaxerror

1
@syntaxerror Se questa risposta risolve il problema, ti preghiamo di dedicare un minuto e fare clic sul segno di spunta sotto il conteggio dei voti a sinistra, questo indicherà a tutti che il problema è stato risolto ed è il modo in cui i ringraziamenti sono espressi nel sito. Ho visto che solo alcune delle altre domande a cui hai risposto hanno accettato le risposte, potresti volerle rivedere anche quelle.
Anthon,

No, è semplicemente scopo. Spesso aspetterei diverse settimane o talvolta 2 mesi prima di accettare una risposta. Il motivo è che alcune persone molto competenti hanno un programma MOLTO carico e potrebbero trovare il tempo per dare la loro (di solito migliore) risposta dopo un paio di settimane. Quindi trovo sempre rispettoso aspettare che si fermino. Bene, e se davvero non lo faranno, non esiterò a colpire il segno di spunta, di sicuro. Inoltre, non vedo perché alcune persone abbiano sempre così tanta fretta su SE + i suoi sapori. Facile, ragazzi. Non saltare la pistola. :) Questo non è il tuo capo che ti preme addosso.
syntaxerror,

10

Inoltre, se in genere prevedi di evitare sovrascritture accidentali in futuro, c'è l' -iopzione per mv. Personalmente non posso pensare ad alcun inconveniente se tu

alias mv='mv -i'

Se hai bisogno di sovrascrivere qualcosa, passa semplicemente l' -fopzione.

Gli alias hanno effetto solo se si digita il comando direttamente in una shell interattiva, non per casi come l'invocazione di find. Avresti potuto correre

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

e quindi ti sarebbe stato richiesto se mvavessi tentato di sovrascrivere un file esistente.


4
Oppure - come probabilmente non sapevi - digitare ´ \ mv `invece di mv. Questo "trucco" meno noto farà sì che il comando con la barra rovesciata ad esso ignorata qualsiasi definizione di alias.
syntaxerror,

Naturalmente, se stai davvero parlando find ... -exec mv ..., dovrai creare ~/bin/mv(o qualche altra directory appropriata) e farlo /bin/mv -i "$@"- perché find ... -execnon guarda gli alias.
G-Man dice "Ripristina Monica" il

In questo caso, preferirei env mv. Meno battitura a macchina. :) Dato che il mio layout di tastiera locale richiede che il tasto MAIUSC venga premuto per una barra, preferirei sempre le versioni "senza barra" (se applicabile).
syntaxerror

1
@syntaxerror: A proposito, quando rispondi al commento (in un nuovo commento), è convenzionale menzionare il nome dell'autore, preceduto da "@", come in "@ G-Man". In questo modo ricevo una notifica. (Sono stato in grado di rispondere al tuo ultimo commento in modo semi-tempestivo perché sono stato avvisato dal commento di Jenny D.) Puoi abbreviare o utilizzare un nome intero (senza spazi), ad esempio "@ StéphaneChazelas". L'autore di un post viene automaticamente avvisato dei commenti a quel post. Vedi i paragrafi Risposte nei commenti di questa pagina di aiuto .
G-Man dice "Ripristina Monica" il

1
Mi dispiace, sono stato senza Internet per un giorno. @ G-Man Concordo sul fatto che avere una versione personale di mvuna location all'inizio $PATHsarebbe una soluzione più pulita. D'altra parte, capita in modo mvimprudente nella shell interattiva (perché succede velocemente). Nel momento in cui compongo qualcosa di più complesso, come findo un forciclo, o anche uno script di shell, tendo a eseguire alcune tirature a secco (usando echo) per assicurarmi di non interrompere qualcosa. In quei casi non ho bisogno di una mano mv, perché ci sto già riflettendo.
ayekat,

7

Oltre alle eccellenti risposte di cui sopra, vorrei chiarire perché non hai ricevuto la domanda se spostare i file o meno.

Se sposti un file con un nuovo nome e quel nome non è una directory, mvrinominerai il tuo file con il nuovo nome.

Il problema qui è che stavi usando findper eseguire mvuna volta per file , non una volta per tutti i file .

Se invece l'avessi fatto mv *90.mp3 90, allora mv sarebbe riuscito con il messaggio di errore che "il file di destinazione non è una directory".

Un altro consiglio è di usare il completamento della scheda quando si digita il percorso target. Ti mostrerà se la destinazione è una directory aggiungendo /al nome della destinazione. È inoltre possibile utilizzare mv -iper essere chiesto se si desidera sovrascrivere un file esistente.


Se invece l'avessi fatto mv *90.mp3 90, allora mv avrebbe fallito con il messaggio di errore che "il file di destinazione non è una directory". Ah, sì, perché così complicato eh? Uso la tua linea e sarò felice. Solo in questo banale caso, però. :) Perché questo è il modo normale in cui faccio le mie domande: le farei restringere per semplicità. Nessuno ha obiettato findfinora considerando che i file degli anni '90 potrebbero anche essere sparsi in varie sottodirectory che voglio anche "catturare". Se e solo se si trovano sempre in una directory di origine, la mvlinea è applicabile.
syntaxerror

@syntaxerror Molto vero - intendevo questo come un esempio di come mvsi comporta, non come una critica alla tua scelta di strumenti.
Jenny D,

1
Probabilmente potresti costruire qualcosa combinando finde mv, ad esempio find /music -type d -exec mv {}/*90.mp3 targetdir\;- ma ora mi sento un po 'come se stessi complicando troppo, e semplicemente usando -io -tè più efficiente
Jenny D

1
@Jenny: se il tuo find . -type d -exec mv {}/*9?.mp3 target \;esempio avesse funzionato, ci sarebbe comunque il rischio che il mvcomando assomigli mv file targeta ogni directory che contiene solo un *9?.mp3file; quindi tutti questi file (tranne l'ultimo) andrebbero persi.
G-Man dice "Ripristina Monica" il

4
@syntaxerror: plaudo al tuo sforzo di esporre la parte essenziale del tuo problema, piuttosto che l'intero script di 42.000 righe in cui si verifica. Ma, anche se volessi fare qualcosa per tutti i *.mp3file in un albero di directory, potresti shopt -s globstare quindi eseguire il tuo comando **/*.mp3- **agirà come un find.
G-Man dice "Ripristina Monica" il

1

Come strategia alternativa per scopi generali vorrei suggerire di trasformare questo tipo di operazione in una sceneggiatura temporanea. Preferisco guardare i risultati finde trasformarli in un mvcomando a mano, assicurandomi di capire cosa sto facendo prima di eseguire. per esempio.

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

Ora posso consultare un elenco di nomi di file e riscrivere il contenuto del file come comando shell.

  • Metti il ​​contenuto del file su una riga: ggVGJ
  • anteporre: Imv [esc]
  • Aggiungere: Asomedir/ [esc]
  • Salva il file. Leggilo di nuovo Prendi un respiro.
  • Eseguire source tmpdalla riga di comando.

È una strategia conservativa, ma sono stato morso troppo spesso da errori di digitazione -execo sedcomandi, o fraintese espansioni della shell, e preferisco adottare un approccio lento e coerente.

In altre parole: sono troppo codardo da usare -exec.


1
Hai lasciato il :%s/.*/"&"/passaggio - perché, in questo caso, sai che ogni nome file contiene almeno uno spazio.
G-Man dice "Ripristina Monica" il

1
Questo ti morderà se i nomi dei file contengono spazi o altri caratteri speciali. Devi citarli correttamente. La revisione di un elenco di comandi non è particolarmente probabile che rilevi errori. Ci sono modi molto migliori per rivedere i comandi prima di eseguirli, come eseguire echo mvinvece di mv, e quindi rimuovere il echose sei soddisfatto.
Gilles 'SO- smetti di essere malvagio' il

Individuare un errore come i nomi di file con spazi è esattamente ciò con cui questa tecnica aiuterà :-)
Tom Rees,

0

Un'altra opzione:

-n, --no-clobber non sovrascrive un file esistente

è lo stesso di -i, ma non chiederà, fallirà.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.