C'è un modo per fare in modo che mv crei la directory da spostare se non esiste?


275

Quindi, se sono nella mia home directory e voglio spostare foo.c in ~ / bar / baz / foo.c, ma quelle directory non esistono, c'è un modo per creare automaticamente quelle directory, in modo che dovresti solo digitare

mv foo.c ~/bar/baz/ 

e tutto funzionerebbe? Sembra che tu possa alias mv a un semplice script bash che verificherebbe se quelle directory esistessero e se no chiamasse mkdir e poi mv, ma ho pensato di controllare se qualcuno avesse avuto un'idea migliore.


Risposte:


294

Che ne dici di questo one-liner (in bash):

mkdir --parents ./some/path/; mv yourfile.txt $_

Abbattendo quello:

mkdir --parents ./some/path

crea la directory (comprese tutte le directory intermedie), dopo di che:

mv yourfile.txt $_

sposta il file in quella directory ($ _ si espande all'ultimo argomento passato al comando shell precedente, ovvero: la directory appena creata).

Non sono sicuro di quanto funzionerà in altre shell, ma potrebbe darti alcune idee su cosa cercare.

Ecco un esempio usando questa tecnica:

$ > ls
$ > touch yourfile.txt
$ > ls
yourfile.txt
$ > mkdir --parents ./some/path/; mv yourfile.txt $_
$ > ls -F
some/
$ > ls some/path/
yourfile.txt

32

1
Perché l'ossessione per il ridondante ./ovunque? Gli arg non sono comandi in PERCORSO senza la .directory ...
Jens,

3
per i neofiti, i genitori possono essere abbreviati in -p
sakurashinken,

8
Stranamente, --parentsnon è disponibile su macOS 10.13.2. È necessario utilizzare -p.
Joshua Pinter,

1
Non funziona se si sposta un file, ad esempio ./some/path/foo. In questo caso il comando dovrebbe essere:mkdir -p ./some/path/foo; mv yourfile.txt $_:h
hbilly

63
mkdir -p `dirname /destination/moved_file_name.txt`  
mv /full/path/the/file.txt  /destination/moved_file_name.txt

2
Sono l'unico a capire che questo codice non ha senso? Si crea ricorsivamente il percorso assoluto al file esistente (il che significa che questo percorso esiste già) e quindi si sposta il file in una posizione (che nel tuo esempio non esiste).
vdegenne,

1
@ user544262772 GNU coreutils ' dirnamenon richiede che il suo argomento esista, è pura manipolazione delle stringhe. Non posso parlare per altre distribuzioni. Niente di sbagliato in questo codice afaict.
TroyHurts,

Questa è la risposta che è più facilmente automatizzata, credo. for f in *.txt; do mkdir -p `dirname /destination/${f//regex/repl}`; mv "$f" "/destination/${f//regex/repl}; done
Kyle,

23

Salva come uno script chiamato mv o mv.sh

#!/bin/bash
# mv.sh
dir="$2"
tmp="$2"; tmp="${tmp: -1}"
[ "$tmp" != "/" ] && dir="$(dirname "$2")"
[ -a "$dir" ] ||
mkdir -p "$dir" &&
mv "$@"

Oppure inserisci alla fine del tuo file ~ / .bashrc come una funzione che sostituisce il mv predefinito su ogni nuovo terminale. L'uso di una funzione consente a bash di mantenerlo memoria, invece di dover leggere un file di script ogni volta.

function mv ()
{
    dir="$2"
    tmp="$2"; tmp="${tmp: -1}"
    [ "$tmp" != "/" ] && dir="$(dirname "$2")"
    [ -a "$dir" ] ||
    mkdir -p "$dir" &&
    mv "$@"
}

Questi si basano sulla presentazione di Chris Lutz.


3
Questo risponde alla domanda più accuratamente IMHO. L'utente vorrebbe un mvcomando avanzato . Particolarmente utile per gli script, ovvero non è necessario eseguire i controlli ed eseguirli mkdir -pogni volta che è necessario utilizzarli mv. Ma dal momento che vorrei il comportamento dell'errore predefinito per mv, ho cambiato il nome della funzione in mvp- in modo da sapere quando avrei potuto creare directory.
Brian Duncan,

Sembra la migliore idea di soluzione, ma l'implementazione si blocca molto.
Ulises Layera,

2
@UlisesLayera Ho modificato l'algoritmo per renderlo più robusto. Non dovrebbe andare in crash adesso
Sepero,

Qualcuno potrebbe spiegare il significato di [ ! -a "$dir" ]Ho condotto esperimenti con la metà destra vera e falsa, entrambi valutati come veri .. ??? Per amor di altri, tmp = "$ {tmp: -1}" sembra afferrare l'ultimo carattere del file per assicurarsi che non sia un percorso (/)
bazz,

@bazz Dovrebbe essere "se $ dir non esiste, allora continua". Sfortunatamente, hai ragione, c'è un bug quando si usa "! -A" e non so perché. Ho modificato un po 'il codice e ora dovrebbe funzionare.
Sepero,

13

Puoi usare mkdir:

mkdir -p ~/bar/baz/ && \
mv foo.c ~/bar/baz/

Un semplice script per farlo automaticamente (non testato):

#!/bin/sh

# Grab the last argument (argument number $#)    
eval LAST_ARG=\$$#

# Strip the filename (if it exists) from the destination, getting the directory
DIR_NAME=`echo $2 | sed -e 's_/[^/]*$__'`

# Move to the directory, making the directory if necessary
mkdir -p "$DIR_NAME" || exit
mv "$@"

Quando eseguo "$ dirname ~? Bar / baz /", ottengo "/ home / dmckee / bar", che non è quello che vuoi qui ...
dmckee --- ex gattino moderatore

@dmckee, Ah, hai ragione. Qualche idea su come risolverlo? Se si inserisce ~ / bar / baz si desidera (a) copiare in ~ / bar / e rinominare in baz, oppure (b) copiare in ~ / bar / baz /. Qual è lo strumento migliore per il lavoro?
Strager

@dmckee, ho usato regexp / sed per trovare una soluzione. Funziona a tuo piacimento? =]
strager

Bene, tranne $ 2 non è l'ultimo argomento a meno che $ # = 2. Ho un programma, la, che stampa il suo ultimo argomento. Lo usavo in una versione del comando cp (per aggiungere la directory corrente se l'ultimo argomento non era una directory). Anche le shell moderne supportano solo $ 1 .. $ 9; uno script Perl potrebbe essere migliore.
Jonathan Leffler,

@Leffler, Non è vero - so che bash supporta più di 9 argomenti (usare $ {123} è un metodo). Non conosco Perl, quindi sentiti libero di rispondere da solo. =]
strager

7

Sembra che la risposta sia no :). Non voglio davvero creare un alias o una funzione solo per farlo, spesso perché è una tantum e sono già nel mezzo della digitazione del mvcomando, ma ho trovato qualcosa che funziona bene per quello:

mv *.sh  shell_files/also_with_subdir/ || mkdir -p $_

Se mvfallisce (dir non esiste), creerà la directory (che è l'ultimo argomento del comando precedente, così $_ha). Quindi basta eseguire questo comando, quindi upeseguirlo nuovamente, e questa volta mvdovrebbe avere esito positivo.


5

Il seguente script di shell, forse?

#!/bin/sh
if [[ -e $1 ]]
then
  if [[ ! -d $2 ]]
  then
    mkdir --parents $2
  fi
fi
mv $1 $2

Questa è la parte base. Potresti voler aggiungere un po 'per verificare la presenza di argomenti e potresti voler cambiare il comportamento se la destinazione esiste o la directory di origine esiste o non esiste (ovvero non sovrascrivere qualcosa che non esiste) .


1
Con il tuo codice, lo spostamento non viene eseguito se la directory non esiste!
Strager

1
E hai perso la bandiera "-p" su mkdir.
dmckee --- ex gattino moderatore,

Se una directory non esiste, perché crearla se hai intenzione di spostarla? (-p flag risolto)
Chris Lutz,

Voglio dire, quando si esegue lo script e la directory $ 2 non esiste, viene creata ma il file non viene copiato.
Strager

ha dato un costrutto semplice e ha aggiunto informazioni su come dovrebbe essere espanso. Perché votarlo ?!
ypnos,

5

Il comando rsync può fare il trucco solo se l'ultima directory nel percorso di destinazione non esiste, ad esempio per il percorso di destinazione di ~/bar/baz/se baresiste ma baznon esiste , è possibile utilizzare il seguente comando:

rsync -av --remove-source-files foo.c ~/bar/baz/

-a, --archive               archive mode; equals -rlptgoD (no -H,-A,-X)
-v, --verbose               increase verbosity
--remove-source-files   sender removes synchronized files (non-dir)

In questo caso la bazdirectory verrà creata se non esiste. Ma se entrambi bare baznon esistono, rsync fallirà:

sending incremental file list
rsync: mkdir "/root/bar/baz" failed: No such file or directory (2)
rsync error: error in file IO (code 11) at main.c(657) [Receiver=3.1.2]

Quindi in pratica dovrebbe essere sicuro usare rsync -av --remove-source-filescome alias per mv.


4

Il modo più semplice per farlo è:

mkdir [directory name] && mv [filename] $_

Supponiamo di aver scaricato i file pdf situati nella mia directory di download ( ~/download) e di volerli spostare in una directory che non esiste (diciamo my_PDF).

Digiterò il seguente comando (assicurandomi che la mia directory di lavoro corrente sia ~/download):

mkdir my_PDF && mv *.pdf $_

È possibile aggiungere -pun'opzione a mkdirse si desidera creare sottodirectory proprio in questo modo: (suppongo di voler creare una sottodirectory denominata python):

mkdir -p my_PDF/python && mv *.pdf $_

2

Modo più sciocco, ma funzionante:

mkdir -p $2
rmdir $2
mv $1 $2

Crea la directory con mkdir -p includendo una directory temporanea che condivida il nome del file di destinazione, quindi rimuovi quella directory del nome file con un semplice rmdir, quindi sposta il file nella sua nuova destinazione. Penso che rispondere usando dirname sia probabilmente il migliore però.


Sì, un po 'sciocco. :-) Se $ 2 è una directory (come nella domanda originale), allora fallisce miseramente, poiché l'ultima directory verrà eliminata, quindi' mv 'fallirà. E se $ 2 esiste già, prova anche a eliminarlo.
Tylla,

Grazie! Questo è esattamente quello che dovevo realizzare: mv ./old ./subdir/new dove subdir non esiste ancora
Mike Murray il

2
((cd src-path && tar --remove-files -cf - files-to-move) | ( cd dst-path && tar -xf -))

1

Codice:

if [[ -e $1 && ! -e $2 ]]; then
   mkdir --parents --verbose -- "$(dirname -- "$2")"
fi
mv --verbose -- "$1" "$2"

Esempio:

argomenti: "d1" "d2 / sub"

mkdir: created directory 'd2'
renamed 'd1' -> 'd2/sub'

1

Questo si sposterà foo.cnella nuova directory bazcon la directory padre bar.

mv foo.c `mkdir -p ~/bar/baz/ && echo $_`

L' -popzione per mkdircreare directory intermedie come richiesto.
Senza -ptutte le directory nel prefisso del percorso devono già esistere.

Tutto all'interno dei backtick ``viene eseguito e l'output viene restituito in linea come parte del comando.
Poiché mkdirnon restituisce nulla, solo l'output di echo $_verrà aggiunto al comando.

$_fa riferimento all'ultimo argomento al comando precedentemente eseguito.
In questo caso, restituirà il percorso alla nuova directory ( ~/bar/baz/) passata al mkdircomando.


Ho decompresso un archivio senza fornire una destinazione e volevo spostare tutti i file tranne demo-app.zipdalla mia directory corrente in una nuova directory chiamata demo-app.
La seguente riga fa il trucco:

mv `ls -A | grep -v demo-app.zip` `mkdir -p demo-app && echo $_`

ls -Arestituisce tutti i nomi di file inclusi i file nascosti ( tranne quelli impliciti .e.. ).

Il simbolo pipe |viene utilizzato per reindirizzare l'output del lscomando a grep( un'utilità di ricerca di testo semplice da riga di comando ).
Il -vflag indica grepdi trovare e restituire tutti i nomi dei file esclusi demo-app.zip.
Tale elenco di file viene aggiunto alla nostra riga di comando come argomenti di origine al comando move mv. L'argomento target è il percorso della nuova directory passata mkdirall'utilizzo referenziato $_e all'output echo.


1
Questo è un buon livello di dettaglio e spiegazione, e soprattutto per un primo post. Grazie!
Jeremy Caney

Grazie, @JeremyCaney! Sono entusiasta di iniziare ad aiutare!
Evan Wunder,

0

Puoi anche usare le estensioni di parentesi graffe:

mkdir -p directory{1..3}/subdirectory{1..3}/subsubdirectory{1..2}      
  • che crea 3 directory (directory1, directory2, directory3),
    • e in ognuna di esse due sottodirectory (sottodirectory1, sottodirectory2),
      • e in ognuna di esse due sottosubdirectory (subsubdirectory1 e subsubdirectory2).

Devi usare bash 3.0 o versioni successive.


5
Interessante, ma non risponde alla domanda del PO.
Alexey Feldgendler,

0
$what=/path/to/file;
$dest=/dest/path;

mkdir -p "$(dirname "$dest")";
mv "$what" "$dest"

1
per renderlo lo script sh autonomo, basta sostituire $ dest e $ src con $ 1 e $ 2
cab404

0

La mia soluzione a una stringa:

test -d "/home/newdir/" || mkdir -p "/home/newdir/" && mv /home/test.txt /home/newdir/

0

Puoi concatenare i comandi: mkdir ~/bar/baz | mv foo.c ~/bar/baz/
oppure lo script della shell è qui:

#!/bin/bash
mkdir $2 | mv $1 $2

1. Apri qualsiasi editor di testo
2. Copia-incolla lo script della shell.
3. Salvalo come mkdir_mv.sh
4. Inserisci la directory del tuo script: cd your/script/directory
5. Cambia modalità file : chmod +x mkdir_mv.sh
6. Imposta alias : alias mv="./mkdir_mv.sh"
Ora, ogni volta che esegui il comando, la mvdirectory di spostamento verrà creata se non esiste.


0

Basato su un commento in un'altra risposta, ecco la mia funzione di shell.

# mvp = move + create parents
function mvp () {
    source="$1"
    target="$2"
    target_dir="$(dirname "$target")"
    mkdir --parents $target_dir; mv $source $target
}

Includilo in .bashrc o simili in modo da poterlo usare ovunque.

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.