Bagaimana Anda memindahkan semua file (termasuk tersembunyi) dari satu direktori ke direktori lainnya?

135

Bagaimana cara memindahkan semua file dalam direktori (termasuk yang tersembunyi) ke direktori lain?

Misalnya, jika saya memiliki folder "Foo" dengan file ".hidden" dan "notHidden" di dalam, bagaimana cara memindahkan kedua file ke direktori bernama "Bar"? Berikut ini tidak berfungsi, karena file ".hidden" tetap di "Foo".

mv Foo/* Bar/

Cobalah sendiri.

mkdir Foo
mkdir Bar
touch Foo/.hidden
touch Foo/notHidden
mv Foo/* Bar/
Cory Klein
sumber
Bah, saya tahu saya harus menyegarkan jawaban sebelum mengetikkan jawaban saya. Tautan yang sangat membantu.
Steven D
Sedihnya, pertanyaan ini berakhir di sini karena saya katakan pada SO bahwa itu harus pindah ke sini atau SU, jadi tentu saja pindah ke sini ketika sudah ada duplikat pada SU sudah :)
Michael Mrozek
Secara pribadi, saya pikir * nix pertanyaan dan jawaban spesifik harus di luar topik pada SU. Dengan aturan saat ini kita berakhir dengan banyak tabrakan konten antara keduanya - seperti pertanyaan ini.
Cory Klein

Jawaban:

154

Zsh

mv Foo/*(DN) Bar/

atau

setopt -s glob_dots
mv Foo/*(N) Bar/

(Biarkan (N)jika Anda tahu direktori tidak kosong.)

Pesta

shopt -s dotglob nullglob
mv Foo/* Bar/

Ksh93

Jika Anda tahu direktori tersebut tidak kosong:

FIGNORE='.?(.)'
mv Foo/* Bar/

Standar (POSIX) sh

for x in Foo/* Foo/.[!.]* Foo/..?*; do
  if [ -e "$x" ]; then mv -- "$x" Bar/; fi
done

Jika Anda ingin membiarkan mvperintah mengembalikan status kesalahan meskipun berhasil, itu jauh lebih sederhana:

mv Foo/* Foo/.[!.]* Foo/..?* Bar/

GNU find dan GNU mv

find Foo/ -mindepth 1 -maxdepth 1 -exec mv -t Bar/ -- {} +

Temuan standar

Jika Anda tidak keberatan mengubah ke direktori sumber:

cd Foo/ &&
find . -name . -o -exec sh -c 'mv -- "$@" "$0"' ../Bar/ {} + -type d -prune

Berikut ini lebih detail tentang cara mengendalikan apakah file dot dicocokkan dengan bash, ksh93 dan zsh.

Pesta

Setel dotglobopsi .

$ echo *
none zero
$ shopt -s dotglob
$ echo *
..two .one none zero

Ada juga GLOBIGNOREvariabel yang lebih fleksibel , yang dapat Anda atur ke daftar pola wildcard yang dipisahkan oleh usus besar untuk diabaikan. Jika tidak disetel (pengaturan default), shell berperilaku seolah-olah nilainya kosong jika dotglobdiatur, dan seolah-olah nilainya adalah .*jika opsi tidak disetel. Lihat Ekspansi Nama File di manual. Direktori meresap .dan ..selalu dihilangkan, kecuali jika .dicocokkan secara eksplisit oleh pola.

$ GLOBIGNORE='n*'
$ echo *
..two .one zero
$ echo .*
..two .one
$ unset GLOBIGNORE
$ echo .*
. .. ..two .one
$ GLOBIGNORE=.:..
$ echo .*
..two .one

Ksh93

Atur FIGNOREvariabelnya . Jika tidak disetel (pengaturan default), shell berperilaku seolah-olah nilainya .*. Untuk mengabaikan .dan .., mereka harus dicocokkan secara eksplisit (manual dalam ksh 93s + 2008-01-31 menyatakan bahwa .dan ..selalu diabaikan, tetapi ini tidak dengan benar menggambarkan perilaku aktual).

$ echo *
none zero
$ FIGNORE='@(.|..)'
$ echo *
..two .one none zero
$ FIGNORE='n*'
$ echo *
. .. ..two .one zero

Anda dapat memasukkan file dot dalam suatu pola dengan mencocokkannya secara eksplisit.

$ unset FIGNORE
$ echo @(*|.[^.]*|..?*)
..two .one none zero

Agar ekspansi keluar kosong jika direktori kosong, gunakan Nopsi pencocokan pola: ~(N)@(*|.[^.]*|..?*)atau ~(N:*|.[^.]*|..?*).

Zsh

Setel dot_globopsi .

% echo *
none zero
% setopt dot_glob
% echo *
..two .one none zero

.dan ..tidak pernah cocok, bahkan jika polanya cocok dengan pemimpin .secara eksplisit.

% echo .*
..two .one

Anda dapat menyertakan file dot dalam pola tertentu dengan D kualifikasi glob .

% echo *(D)
..two .one none zero

Tambahkan Nkualifikasi glob untuk membuat ekspansi keluar kosong di direktori kosong: *(DN).


Catatan: Anda mungkin mendapatkan hasil ekspansi nama file dalam urutan yang berbeda (misalnya, nonediikuti oleh .onediikuti oleh ..two) berdasarkan pengaturan Anda dari LC_COLLATE, LC_ALL, dan LANGvariabel.

Gilles
sumber
1
Mengapa nullglob? Akan lebih masuk akal untuk membatalkan mvperintah (seperti ikan, csh / tcsh, zsh (tanpa (N)) lakukan, atau bash dengan failglob) daripada menjalankan mv /Bar/perintah yang tidak masuk akal.
Stéphane Chazelas
1
Menurut saya uraian GLOBIGNORE Anda tidak akurat dan menyesatkan. Menyetel GLOBIGNORE ke nilai yang tidak kosong akan mengaktifkan dotglob dan menjadikannya .dan ..tidak pernah cocok (seperti di shell yang masuk akal lainnya seperti zsh, fish, pdksh, dan turunannya) bahkan jika Anda mematikan dotglob setelah itu. ( GLOBIGNORE=:; shopt -u dotglob; echo .*tidak akan menampilkan .dan ..). pengaturan GLOBIGNORE untuk .:..atau .atau :memiliki efek yang sama.
Stéphane Chazelas
ksh93selalu mengabaikan .dan ..tampaknya telah rusak di antara ksh93k+(di mana ia bekerja) dan ksh93m(di mana itu tidak lagi berfungsi). Perhatikan bahwa ini buruk karena ksh93akan mengambil nilai FIGNORE dari lingkungan, jadi FIGNORE='!(..)'env var misalnya dapat membuat kekacauan.
Stéphane Chazelas
24
#!/bin/bash

shopt -s dotglob
mv Foo/* Bar/

Dari man bash

dotglob Jika diset, bash menyertakan nama file yang diawali dengan '.' dalam hasil ekspansi pathname.

SiegeX
sumber
Dengan peringatan bahwa perintah ini mengembalikan kode kesalahan jika direktori itu kosong (meskipun perintah itu benar-benar dilakukan sebagaimana dimaksud).
Gilles
1
@Gilles, kesalahannya adalah karena mvmengeluh bahwa file yang dipanggil Foo/*tidak ada. Menariknya, jika Foodapat dicari, tetapi tidak dapat dibaca, dan ada file yang dipanggil *di sana, Anda tidak akan mendapatkan kesalahan, file itu akan dipindahkan, tetapi tidak yang lain dalam direktori. bashmemiliki failglobopsi sehingga berperilaku lebih seperti zsh, fishatau csh/ tcshdan membatalkan perintah dengan kesalahan ketika gumpalan tidak dapat diperluas, bukan itu perilaku palsu meninggalkan gumpalan apa adanya.
Stéphane Chazelas
8

Cara sederhana untuk melakukan ini bashadalah

mv {Foo/*,Foo/.*} Bar/

Tetapi ini juga akan memindahkan direktori.

Jika Anda ingin memindahkan semua file termasuk tersembunyi tetapi tidak ingin memindahkan direktori apa pun, Anda dapat menggunakan for for dan uji.

for i in $(ls -d {Foo/*,Foo/.*});do test -f $i && mv -v $i Bar/; done;

Bulan Boris
sumber
4

Salah satu caranya adalah menggunakan find:

find Foo/ -type f -exec mv -t Bar/ {} \+

The -type fmembatasi perintah find untuk mencari file. Anda harus menyelidiki -type, -maxdepthdan -mindepthpilihan dari finduntuk menyesuaikan perintah Anda ke akun untuk subdirektori. Temukan memiliki halaman manual yang panjang tapi sangat membantu .

Steven D
sumber
3
Itu hanya memindahkan file biasa Foo/, bukan subdirektori dan file lainnya.
Gilles
2

Coba perintah salin cp:

$ cp -r myfolder/* destinationfolder

cp -r berarti salin rekursif, sehingga semua folder dan file akan disalin.

Anda dapat menggunakan perintah rmhapus untuk menghapus folder:

$ rm -r myfolder

Lihat lebih lanjut: pindahkan semua file dari direktori ke yang lain .

ucefkh
sumber
2
Bukan yang terbaik ketika Anda memindahkan banyak file besar, tapi saya kira itu akan berhasil.
Cory Klein
Mungkin jika Anda menggunakan titik, bukan bintang?
vincent
1

Rsync adalah pilihan lain:

rsync -axvP --remove-source-files sourcedirectory/ targetdirectory

Ini berfungsi karena di rsync trailing slash penting, sourcedirectory/merujuk pada isi direktori, sementara sourcedirectoryakan merujuk ke direktori itu sendiri.

Kerugian dari metode ini adalah rsync hanya akan membersihkan file setelah pindah, bukan direktori. Jadi Anda dibiarkan dengan pohon direktori sumber kosong. Untuk pemecahannya, lihat:

Pindahkan file dan hapus direktori dengan rsync?

Jadi, meskipun ini mungkin tidak optimal untuk memindahkan operasi, ini bisa sangat berguna untuk operasi penyalinan.

Grumbel
sumber
1

Jawaban untuk bash / ikan

Berikut cara untuk melakukannya menggunakan wildcard:

.[!.]* ..?*akan cocok dengan semua file yang tersembunyi kecuali .dan..

.[!.]* ..?* *akan cocok dengan semua file (tersembunyi atau tidak) kecuali .dan..

Dan untuk menjawab contoh khusus dari pertanyaan ini, Anda perlu cd foo && mv .[!.]* ..?* * ../bar

Penjelasan

.[!.]*cocok dengan nama file yang dimulai dengan satu titik, diikuti oleh karakter apa pun kecuali titik yang secara opsional diikuti oleh string apa pun. Ini cukup dekat tetapi melewatkan file dimulai dengan dua titik seperti ..foo. Untuk menyertakan file seperti kita menambahkan ..?*yang cocok berkas-nama dimulai dengan dua titik, diikuti oleh karakter apapun, opsional diikuti beberapa string.

Uji

Anda dapat menguji wildcard ini dengan perintah di bawah ini. Saya sudah mencoba mereka dengan sukses di bawah bash dan ikan. Mereka gagal di bawah sh, zsh, xonsh.

mkdir temp
cd temp
touch  a  .b  .bc  ..c  ..cd  ...d  ...de
ls .[!.]* ..?*
# you get this output:
          .b  .bc  ..c  ..cd  ...d  ...de
# cleanup
cd ..
rm -rf temp
ndemou
sumber
0

Anda mungkin juga dapat menemukan dan menerima backquotes untuk memilih file untuk perintah pindah. Pindahkan itu ke mv.

Yaitu Untuk file tersembunyi

find Foo -maxdepth 1 | egrep '^Foo/[.]' # Output: .hidden

Begitu

mv `find Foo -maxdepth 1 | egrep '^Foo/[.]'` Bar # mv Foo/.hidden Bar

Memindahkan hanya file tersembunyi yang dipilih ke Bar:

mv `find Foo -maxdepth 1 | egrep '^Foo/.'` Bar # mv Foo/.hidden Foo/notHidden Bar

Memindahkan semua file di Foo ke Bar sejak '.' dalam perintah egrep bertindak sebagai wildcard tanpa tanda kurung.

The ^karakter memastikan pertandingan dimulai dari awal baris.

Beberapa detail egreppencocokan pola dapat ditemukan di sini .

Menggunakan maxdepth 1stop ditemukan dari masuk ke subdirektori.

pengguna3192145
sumber
0

Terinspirasi dari jawaban ini :

Tanpa menyalin file ...

rsync -ax --link-dest=../Foo/ Foo/ Bar

Peringatan:

  • --link-destpath harus absolut atau relatif terhadap TUJUAN ( Bar dalam contoh)

  • Anda harus menggunakan /SOURCE ( Foo/dalam contoh), jika tidak maka akan menyalin folder SOURCE alih-alih kontennya.

palindrom
sumber
0

Saya menemukan bahwa ini bekerja dengan baik untuk bash dan tidak perlu mengubah opsi shell

mv sourcedir/{*,.[^.]*} destdir/

SUNTING:

Jadi seperti yang dikatakan G-man, jawaban asli saya tidak sesuai dengan posix dan hampir sama dengan jawaban ndemou di atas dengan satu perubahan, yaitu menggunakan ekspansi brace untuk membuat daftar string yang kemudian ditindaklanjuti. Ini berarti Anda tidak perlu cdmasuk ke direktori sumber. Sebenarnya bukan perubahan besar, tetapi berbeda.

contoh: katakanlah Anda sudah memiliki tata letak berikut.

 $ tree -a
.
├── destdir
└── sourcedir
    ├── ..d1
    ├── ..d2
    ├── ..double
    ├── file-1
    ├── file-2
    ├── .hidden-1
    ├── .hidden-2
    ├── ...t1
    └── ...t2

Pertanyaan asli hanya menyebutkan file tersembunyi dengan satu periode, tetapi katakanlah ada beberapa dengan dua atau lebih periode di awal nama. Anda bisa menambahkan ekspresi tambahan ke dalam kawat gigi. Kami kemudian dapat mengeksekusi

mv sourcedir/{*,.[!.]*,..?*} destdir/

Ini akan diperluas ke yang berikut:

mv sourcedir/file-1 sourcedir/file-2 sourcedir/.hidden-1 sourcedir/.hidden-2 sourcedir/..d1 sourcedir/..d2 sourcedir/..double sourcedir/...t1 sourcedir/...t2 destdir/

Anda sekarang akan melihat semua file yang terletak di destdir :

 $ tree -a
.
├── destdir
   ├── ..d1
   ├── ..d2
   ├── ..double
   ├── file-1
   ├── file-2
   ├── .hidden-1
   ├── .hidden-2
   ├── ...t1
   └── ...t2
└── sourcedir

Anda dapat melakukan beberapa hal keren dengan kawat gigi di bash dengan tambahan lebih banyak lagi di 4.x. Lihat bash-hacker untuk beberapa contoh bagus.

cmndr sp0ck
sumber
1
Ini pada dasarnya menggandakan jawaban ndemou kecuali Anda menambahkan kurung kurawal (tanpa menjelaskannya). Tetapi (1) jawaban Anda tidak cocok dengan file yang namanya dimulai .., dan (2) agar sesuai dengan POSIX , Anda harus menggunakannya !sebagai ganti ^; yaitu  {*,.[!.]*},.
G-Man
@ G-Man, maaf soal itu. Jadi Anda benar dan buruk saya karena tidak melihat respons ndemou. Mereka adalah hal yang hampir sama. 1. terima kasih telah menunjukkan versi saya tidak sesuai dengan posix. 2. Saya menggunakan ekspansi brace untuk membuat daftar string yang shell akan gunakan untuk melakukan tindakan. Ini juga berarti saya tidak perlu cdmasuk ke direktori sumber untuk bertindak pada file atau menulis path file yang sama berulang-ulang untuk mengekspresikan path ke semua file. Saya akan memperbarui contoh ini segera untuk memberi pembaca gambaran yang lebih baik tentang apa yang saya bicarakan.
cmndr sp0ck
0

Untuk distro Linux minimal , berikut ini harus bekerja. Pertama, lakukan pemindahan dasar semua (yang merindukan file tersembunyi). Kemudian, pindahkan semua file yang tersembunyi (termasuk .dan ..yang akan sebenarnya tidak akan dipindahkan).

mv /sourceDir/* /destinationDir 2> /dev/null
mv /sourceDir/.* /destinationDir 2> /dev/null

Catatan: Jika tidak ada konten yang terlihat, perintah pertama akan menghasilkan pesan kesalahan. Perintah kedua akan selalu menghasilkan kesalahan yang mengatakan itu tidak bisa bergerak .dan ... Dengan demikian, hanya pipa kesalahan ke /dev/null(seperti yang ditunjukkan).

Jika Anda membutuhkan ini sebagai satu-liner, cukup gabungkan dengan semi-kolon:

mv /sourceDir/* /destinationDir 2> /dev/null; mv /sourceDir/.* /destinationDir 2> /dev/null
Terima kasih
sumber