Apakah ada cara untuk membuat saya membuat direktori untuk dipindahkan jika tidak ada?

275

Jadi, jika saya berada di direktori home saya dan saya ingin memindahkan foo.c ke ~ / bar / baz / foo.c, tetapi direktori-direktori itu tidak ada, apakah ada cara untuk membuat direktori-direktori tersebut secara otomatis dibuat, sehingga Anda hanya perlu mengetik

mv foo.c ~/bar/baz/ 

dan semuanya akan berhasil? Sepertinya Anda bisa alias mv ke skrip bash sederhana yang akan memeriksa apakah direktori tersebut ada dan jika tidak akan memanggil mkdir dan kemudian mv, tapi saya pikir saya akan memeriksa untuk melihat apakah ada yang punya ide yang lebih baik.

Paul Wicks
sumber

Jawaban:

294

Bagaimana dengan one-liner ini (dalam bash):

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

Hancurkan itu:

mkdir --parents ./some/path

membuat direktori (termasuk semua direktori perantara), setelah itu:

mv yourfile.txt $_

memindahkan file ke direktori tersebut ($ _ meluas ke argumen terakhir yang dilewatkan ke perintah shell sebelumnya, yaitu: direktori yang baru dibuat).

Saya tidak yakin seberapa jauh ini akan berhasil di shell lain, tetapi mungkin memberi Anda beberapa ide tentang apa yang harus dicari.

Berikut adalah contoh menggunakan teknik ini:

$ > ls
$ > touch yourfile.txt
$ > ls
yourfile.txt
$ > mkdir --parents ./some/path/; mv yourfile.txt $_
$ > ls -F
some/
$ > ls some/path/
yourfile.txt
KarstenF
sumber
32
Bagus dengan '$ _'.
dmckee --- ex-moderator kitten
1
Mengapa obsesi berlebihan di ./mana-mana? Args bukan perintah di PATH tanpa .direktori ...
Jens
3
untuk para pemula, - orang tua dapat disingkat menjadi -p
sakurashinken
8
Anehnya, --parentstidak tersedia di macOS 10.13.2. Anda harus menggunakan -p.
Joshua Pinter
1
Tidak berfungsi jika memindahkan file misalnya ./some/path/foo. Dalam hal ini perintahnya harus:mkdir -p ./some/path/foo; mv yourfile.txt $_:h
hhbilly
63
mkdir -p `dirname /destination/moved_file_name.txt`  
mv /full/path/the/file.txt  /destination/moved_file_name.txt
Billmc
sumber
2
Saya satu-satunya yang menyadari kode ini tidak masuk akal? Anda membuat secara absolut path absolut ke file yang ada (yang berarti path ini sudah ada) dan kemudian memindahkan file ke lokasi (yang dalam contoh Anda tidak ada).
vdegenne
1
@ user544262772 GNU coreutils ' dirnametidak memerlukan argumennya, itu murni manipulasi string. Tidak dapat berbicara untuk distribusi lainnya. Tidak ada yang salah dengan kode ini.
TroyHurts
Ini adalah jawaban yang paling mudah otomatis, saya pikir. for f in *.txt; do mkdir -p `dirname /destination/${f//regex/repl}`; mv "$f" "/destination/${f//regex/repl}; done
Kyle
23

Simpan sebagai skrip bernama mv atau mv.sh

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

Atau letakkan di akhir file ~ / .bashrc Anda sebagai fungsi yang menggantikan mv default pada setiap terminal baru. Menggunakan fungsi memungkinkan bash menyimpannya dalam memori, alih-alih harus membaca file skrip setiap saat.

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

Ini berdasarkan pada pengajuan Chris Lutz.

Sepero
sumber
3
Ini menjawab pertanyaan IMHO paling akurat. Pengguna menginginkan mvperintah yang ditingkatkan . Terutama berguna untuk skrip, yaitu Anda tidak perlu ingin menjalankan pemeriksaan dan menjalankan mkdir -pkapan saja Anda perlu menggunakan mv. Tapi karena saya ingin perilaku kesalahan default mv, saya mengubah nama fungsi menjadi mvp- sehingga saya tahu kapan saya bisa membuat direktori.
Brian Duncan
Sepertinya ide solusi terbaik, tetapi implementasi banyak crash.
Ulises Layera
2
@UlisesLayera Saya memodifikasi algoritma untuk membuatnya lebih kuat. Seharusnya tidak crash sekarang
Sepero
Bisakah seseorang tolong jelaskan arti dari [ ! -a "$dir" ]saya telah melakukan percobaan dengan bagian kanan menjadi benar dan salah, keduanya dievaluasi menjadi benar .. ??? Demi yang lain, tmp = "$ {tmp: -1}" muncul untuk mengambil karakter terakhir dari file untuk memastikan itu bukan path (/)
bazz
@Bazz Seharusnya melakukan "jika $ dir tidak ada, kemudian lanjutkan". Sayangnya, Anda benar, ada bug saat menggunakan "! -A", dan saya tidak tahu mengapa. Saya telah mengedit kode sedikit, dan sekarang seharusnya berfungsi.
Sepero
13

Anda bisa menggunakan mkdir:

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

Script sederhana untuk melakukannya secara otomatis (belum diuji):

#!/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 "$@"
strager
sumber
Ketika saya menjalankan "$ dirname ~? Bar / baz /", saya mendapatkan "/ home / dmckee / bar", yang bukan itu yang Anda inginkan di sini ...
dmckee --- ex-moderator kitten
@ demckee, Ah, kamu benar. Adakah cara untuk mengatasi ini? Jika Anda memasukkan ~ / bar / baz, Anda (a) ingin menyalin ke ~ / bar / dan mengganti nama menjadi baz, atau (b) menyalin ke ~ / bar / baz /. Apa alat yang lebih baik untuk pekerjaan itu?
strager
@ dmckee, saya telah menggunakan regexp / sed untuk datang dengan solusi. Apakah itu sesuai dengan keinginan Anda? =]
strager
Bagus, kecuali $ 2 bukan argumen terakhir kecuali $ # = 2. Saya punya program, la, yang mencetak argumen terakhirnya. Saya biasa menggunakannya dalam versi perintah cp (untuk menambahkan direktori saat ini jika argumen terakhir bukan direktori). Bahkan cangkang modern hanya mendukung $ 1. $ 9 saja; skrip Perl mungkin lebih baik.
Jonathan Leffler
@Leffler, Tidak benar - Saya tahu bash mendukung lebih dari 9 argumen (menggunakan $ {123} adalah salah satu metode). Saya tidak tahu Perl, jadi silakan jawab sendiri. =]
strager
7

Sepertinya jawabannya tidak :). Saya tidak benar-benar ingin membuat alias atau func hanya untuk melakukan ini, sering karena ini satu kali dan saya sudah di tengah mengetik mvperintah, tetapi saya menemukan sesuatu yang bekerja dengan baik untuk itu:

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

Jika mvgagal (dir tidak ada), itu akan membuat direktori (yang merupakan argumen terakhir ke perintah sebelumnya, begitu $_juga). Jadi jalankan saja perintah ini, lalu upjalankan kembali, dan kali ini mvharus berhasil.

Menepuk
sumber
5

Script shell berikut, mungkin?

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

Itulah bagian dasarnya. Anda mungkin ingin menambahkan sedikit untuk memeriksa argumen, dan Anda mungkin ingin perilaku berubah jika tujuan ada, atau direktori sumber ada, atau tidak ada (yaitu jangan menimpa sesuatu yang tidak ada) .

Chris Lutz
sumber
1
Dengan kode Anda, langkah tersebut tidak dilakukan jika direktori tidak ada!
strager
1
Dan Anda melewatkan bendera "-p" ke mkdir.
dmckee --- ex-moderator kitten
Jika direktori tidak ada, mengapa membuatnya jika Anda hanya akan memindahkannya? (-p flag fix)
Chris Lutz
Maksud saya, ketika Anda menjalankan skrip dan direktori $ 2 tidak ada, itu dibuat tetapi file tidak disalin.
strager
dia memberikan konstruksi sederhana dan menambahkan informasi tentang bagaimana itu harus diperluas. Mengapa memilih itu ?!
ypnos
5

perintah rsync dapat melakukan trik hanya jika direktori terakhir di jalur tujuan tidak ada, misalnya untuk jalur tujuan ~/bar/baz/jika barada tetapi baztidak, maka perintah berikut dapat digunakan:

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)

Dalam hal ini bazdirektori akan dibuat jika tidak ada. Tetapi jika keduanya bardan baztidak ada, rsync akan gagal:

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]

Jadi pada dasarnya itu harus aman digunakan rsync -av --remove-source-filessebagai alias untuk mv.

sepehr
sumber
4

Cara paling sederhana untuk melakukannya adalah:

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

Misalkan saya mengunduh file pdf yang terletak di direktori unduhan saya ( ~/download) dan saya ingin memindahkan semuanya ke direktori yang tidak ada (katakanlah my_PDF).

Saya akan mengetik perintah berikut (memastikan direktori kerja saya saat ini adalah ~/download):

mkdir my_PDF && mv *.pdf $_

Anda dapat menambahkan -popsi mkdirjika Anda ingin membuat subdirektori seperti ini: (seharusnya saya ingin membuat subdirektori bernama python):

mkdir -p my_PDF/python && mv *.pdf $_
Abdoul Seibou
sumber
2

Sillier, tetapi cara kerjanya:

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

Buat direktori dengan mkdir -p termasuk direktori sementara yang membagikan nama file tujuan, kemudian hapus direktori nama file itu dengan rmdir sederhana, kemudian pindahkan file Anda ke tujuan yang baru. Saya pikir jawaban menggunakan dirname mungkin yang terbaik.

Blarn Blarnson
sumber
Yepp, agak konyol. :-) Jika $ 2 adalah direktori (seperti dalam pertanyaan asli) maka gagal total, karena direktori terakhir akan dihapus, jadi 'mv' akan gagal. Dan jika $ 2 sudah ada maka ia juga mencoba menghapusnya.
Tylla
Terima kasih! Inilah yang harus saya selesaikan: mv ./old ./subdir/new di mana subdir belum ada
Mike Murray
2
((cd src-path && tar --remove-files -cf - files-to-move) | ( cd dst-path && tar -xf -))
svaardt
sumber
1

Kode:

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

Contoh:

argumen: "d1" "d2 / sub"

mkdir: created directory 'd2'
renamed 'd1' -> 'd2/sub'
John Doe
sumber
1

Ini akan pindah foo.cke direktori baru bazdengan direktori induk bar.

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

The -ppilihan untuk mkdirakan membuat direktori menengah seperti yang diperlukan.
Tanpa -psemua direktori di jalur awalan harus sudah ada.

Segala sesuatu di dalam backtick ``dieksekusi dan output dikembalikan in-line sebagai bagian dari perintah Anda.
Karena mkdirtidak mengembalikan apa pun, hanya output dari yang echo $_akan ditambahkan ke perintah.

$_referensi argumen terakhir ke perintah yang dieksekusi sebelumnya.
Dalam hal ini, ini akan mengembalikan path ke direktori baru Anda ( ~/bar/baz/) yang diteruskan ke mkdirperintah.


Saya membuka ritsleting arsip tanpa memberikan tujuan dan ingin memindahkan semua file kecuali demo-app.zipdari direktori saya saat ini ke direktori baru yang disebut demo-app.
Baris berikut melakukan trik:

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

ls -Amengembalikan semua nama file termasuk file tersembunyi ( kecuali untuk yang implisit .dan.. ).

Simbol pipa |digunakan untuk menyalurkan output dari lsperintah ke grep( utilitas pencarian teks-baris perintah ).
The -vbendera mengarahkan grepuntuk menemukan dan mengembalikan semua nama file tidak termasuk demo-app.zip.
Daftar file itu ditambahkan ke baris perintah kami sebagai argumen sumber ke perintah pindah mv. Argumen target adalah jalur ke direktori baru yang dilewati untuk mkdirmenggunakan referensi $_dan menggunakan keluaran echo.

Evan Wunder
sumber
1
Ini adalah tingkat detail dan penjelasan yang bagus — dan terutama untuk posting pertama. Terima kasih!
Jeremy Caney
Terima kasih, @JeremyCaney! Saya senang mulai membantu!
Evan Wunder
0

Anda bahkan dapat menggunakan ekstensi brace:

mkdir -p directory{1..3}/subdirectory{1..3}/subsubdirectory{1..2}      
  • yang menciptakan 3 direktori (directory1, directory2, directory3),
    • dan di masing-masing dari mereka dua subdirektori (subdirektori1, subdirektori2),
      • dan di masing-masingnya ada dua subdirektori (subsubdirektori1 dan subsubdirektori2).

Anda harus menggunakan bash 3.0 atau yang lebih baru.

Stefan Gruenwald
sumber
5
Menarik, tetapi tidak menjawab pertanyaan OP.
Alexey Feldgendler
0
$what=/path/to/file;
$dest=/dest/path;

mkdir -p "$(dirname "$dest")";
mv "$what" "$dest"
cab404
sumber
1
untuk membuatnya menjadi skrip sh mandiri, cukup ganti $ dest dan $ src dengan $ 1 dan $ 2
cab404
0

Solusi satu string saya:

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

Anda dapat membuat perintah: mkdir ~/bar/baz | mv foo.c ~/bar/baz/
atau skrip shell ada di sini:

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

1. Buka editor teks apa saja
2. Skrip shell salin-tempel .
3. Simpan sebagai mkdir_mv.sh
4. Masukkan direktori skrip Anda: cd your/script/directory
5. Ubah mode file : chmod +x mkdir_mv.sh
6. Tetapkan alias : alias mv="./mkdir_mv.sh"
Sekarang, setiap kali Anda menjalankan mvdirektori pemindahan perintah akan dibuat jika tidak ada.

Sharofiddin
sumber
0

Berdasarkan komentar pada jawaban lain, inilah fungsi shell saya.

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

Sertakan ini dalam .bashrc atau yang serupa sehingga Anda dapat menggunakannya di mana saja.

Ben Winding
sumber