Làm thế nào để bạn di chuyển tất cả các tập tin (bao gồm cả ẩn) từ một thư mục khác?


133

Làm cách nào để di chuyển tất cả các tệp trong một thư mục (bao gồm cả các tệp bị ẩn) sang thư mục khác?

Ví dụ: nếu tôi có thư mục "Foo" với các tệp ".hidden" và "notHidden" bên trong, làm cách nào để di chuyển cả hai tệp vào thư mục có tên "Bar"? Các mục sau không hoạt động, vì tệp ".hidden" nằm trong "Foo".

mv Foo/* Bar/

Hãy thử nó.

mkdir Foo
mkdir Bar
touch Foo/.hidden
touch Foo/notHidden
mv Foo/* Bar/


Bah, tôi biết tôi nên làm mới câu trả lời trước khi gõ lên. Liên kết rất hữu ích.
Steven D

Đáng buồn thay, câu hỏi này đã kết thúc ở đây vì tôi đã nói trên SO rằng nó nên chuyển đến đây hoặc SU, vì vậy tất nhiên nó đã chuyển đến đây khi đã có một bản sao trên SU rồi :)
Michael Mrozek

Cá nhân, tôi nghĩ rằng * nix câu hỏi và câu trả lời cụ thể nên lạc đề trên SU. Với các quy tắc hiện tại, chúng tôi kết thúc với hàng tấn va chạm nội dung giữa hai người - như câu hỏi này.
Cory Klein

Câu trả lời:


153

Zsh

mv Foo/*(DN) Bar/

hoặc là

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

(Để lại (N)nếu bạn biết thư mục không trống.)

Bash

shopt -s dotglob nullglob
mv Foo/* Bar/

Ksh93

Nếu bạn biết thư mục không trống:

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

Tiêu chuẩn (POSIX) sh

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

Nếu bạn sẵn sàng để mvlệnh trả về trạng thái lỗi mặc dù đã thành công, thì đơn giản hơn nhiều:

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

GNU tìm và GNU mv

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

Tìm tiêu chuẩn

Nếu bạn không thay đổi thư mục nguồn:

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

Dưới đây là chi tiết hơn về việc kiểm soát xem các tệp chấm có khớp trong bash, ksh93 và zsh hay không.

Bash

Đặt dotglobtùy chọn .

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

Ngoài ra còn có GLOBIGNOREbiến linh hoạt hơn mà bạn có thể đặt thành danh sách các mẫu ký tự đại diện được phân tách bằng dấu hai chấm để bỏ qua. Nếu bỏ đặt (cài đặt mặc định), trình bao hoạt động như thể giá trị trống nếu dotglobđược đặt và như thể giá trị là .*nếu tùy chọn không được đặt. Xem Mở rộng tên tệp trong hướng dẫn. Các thư mục phổ biến ...luôn bị bỏ qua, trừ khi .được khớp rõ ràng theo mẫu.

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

Ksh93

Đặt FIGNOREbiến . Nếu không được đặt (cài đặt mặc định), shell sẽ hoạt động như thể giá trị là .*. Để bỏ qua ..., chúng phải được khớp một cách rõ ràng (hướng dẫn trong ksh 93s + 2008-01-31 nói rằng ...luôn bị bỏ qua, nhưng điều này không mô tả chính xác hành vi thực tế).

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

Bạn có thể bao gồm các tệp chấm trong một mẫu bằng cách khớp chúng rõ ràng.

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

Để mở rộng ra trống nếu thư mục trống, sử dụng Ntùy chọn khớp mẫu: ~(N)@(*|.[^.]*|..?*)hoặc ~(N:*|.[^.]*|..?*).

Zsh

Đặt dot_globtùy chọn .

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

...không bao giờ được khớp, ngay cả khi mô hình khớp với hàng đầu .một cách rõ ràng.

% echo .*
..two .one

Bạn có thể bao gồm các tệp chấm trong một mẫu cụ thể với D vòng loại toàn cầu .

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

Thêm Nvòng loại toàn cầu để làm cho việc mở rộng trở nên trống rỗng trong một thư mục trống : *(DN).


Lưu ý: bạn có thể có được kết quả mở rộng tên tập tin trong đơn đặt hàng khác nhau (ví dụ, nonetiếp theo .onetiếp theo ..two) dựa trên các thiết lập của bạn trong những LC_COLLATE, LC_ALLLANGcác biến.


1
Tại sao nullglob? Sẽ tốt hơn nếu hủy bỏ mvlệnh (như cá, csh / tcsh, zsh (không có (N)) làm hoặc bash với failglob) hơn là chạy một mv /Bar/lệnh không có ý nghĩa.
Stéphane Chazelas

1
Tôi muốn nói rằng mô tả GLOBIGNORE của bạn không chính xác và sai lệch. Đặt GLOBIGNORE thành giá trị không trống sẽ bật dotglob và làm cho nó ...không bao giờ khớp (như trong các vỏ hợp lý khác như zsh, fish, pdksh và các dẫn xuất) ngay cả khi bạn tắt dotglob sau đó. ( GLOBIGNORE=:; shopt -u dotglob; echo .*sẽ không đầu ra ...). thiết GLOBIGNORE đến .:..hay .hoặc :có tác dụng tương tự.
Stéphane Chazelas

ksh93luôn luôn phớt lờ ...dường như đã bị phá vỡ ở giữa ksh93k+(nơi nó hoạt động) và ksh93m(nơi nó không còn hoạt động nữa). Lưu ý rằng điều tồi tệ ở chỗ ksh93sẽ lấy giá trị của FIGNORE từ môi trường, vì vậy, FIGNORE='!(..)'ví dụ env var có thể tạo ra sự tàn phá.
Stéphane Chazelas

24
#!/bin/bash

shopt -s dotglob
mv Foo/* Bar/

Từ man bash

dotglob Nếu được đặt, bash bao gồm tên tệp bắt đầu bằng '.' trong kết quả mở rộng tên đường dẫn.


Với lời cảnh báo rằng lệnh này trả về mã lỗi nếu thư mục trống (mặc dù lệnh thực sự được thực hiện như dự định).
Gilles

1
@Gilles, lỗi sau đó sẽ xuất phát từ việc mvphàn nàn rằng tệp được gọi là Foo/*không tồn tại. Thật thú vị, nếu Foocó thể tìm kiếm, nhưng không thể đọc được và có một tệp được gọi *trong đó, bạn sẽ không gặp lỗi, tệp đó sẽ được di chuyển, nhưng không phải là các tệp khác trong thư mục. bashcó một failglobtùy chọn để nó hoạt động giống như zsh, fishhoặc csh/ tcshvà hủy bỏ lệnh có lỗi khi một quả địa cầu không thể được mở rộng thay vì hành vi không có thật đó là rời khỏi toàn cầu.
Stéphane Chazelas

8

Một cách đơn giản để làm điều này bash

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

Nhưng điều này cũng sẽ di chuyển các thư mục.

Nếu bạn muốn di chuyển tất cả các tệp bao gồm ẩn nhưng không muốn di chuyển bất kỳ thư mục nào, bạn có thể sử dụng vòng lặp for và kiểm tra.

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


4

Một cách là sử dụng find:

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

Việc -type fgiới hạn lệnh find để tìm tập tin. Bạn nên điều tra sự -type, -maxdepth-mindepthtùy chọn của findtùy chỉnh lệnh của bạn vào tài khoản cho thư mục con. Tìm có một trang hướng dẫn dài nhưng rất hữu ích .


3
Điều đó chỉ di chuyển các tệp thông thường trong Foo/, không phải các thư mục con và các tệp khác.
Gilles

2

Hãy thử lệnh sao chép cp:

$ cp -r myfolder/* destinationfolder

cp -r có nghĩa là sao chép đệ quy, vì vậy tất cả các thư mục và tệp sẽ được sao chép.

Bạn có thể sử dụng lệnh rmremove để xóa thư mục:

$ rm -r myfolder

Xem thêm: di chuyển tất cả các tệp từ một thư mục sang một thư mục khác .


2
Không phải là tốt nhất khi bạn di chuyển nhiều tệp lớn, nhưng tôi đoán nó sẽ hoạt động.
Cory Klein

Có lẽ nếu bạn sử dụng một dấu chấm thay vì ngôi sao?
vincent

1

Rsync là một tùy chọn khác:

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

Điều này hoạt động vì trong rsync, dấu gạch chéo có vấn đề, sourcedirectory/đề cập đến nội dung của thư mục, trong khi sourcedirectorysẽ đề cập đến chính thư mục đó.

Nhược điểm của phương pháp này là rsync sẽ chỉ dọn sạch các tệp sau khi di chuyển chứ không phải thư mục. Vì vậy, bạn còn lại với một cây sourcedirectory trống. Đối với cách giải quyết cho điều đó, xem:

Di chuyển tập tin và xóa thư mục với rsync?

Vì vậy, trong khi điều này có thể không tối ưu cho các hoạt động di chuyển, nó có thể cực kỳ hữu ích cho các hoạt động sao chép.


1

Trả lời cho bash / cá

Đây là một cách để làm điều đó bằng cách sử dụng ký tự đại diện:

.[!.]* ..?*sẽ khớp với tất cả các tệp ẩn ngoại trừ ...

.[!.]* ..?* *sẽ khớp với tất cả các tệp (ẩn hoặc không) ngoại trừ ...

Và để trả lời ví dụ cụ thể của câu hỏi này, bạn cần cd foo && mv .[!.]* ..?* * ../bar

Giải trình

.[!.]*khớp với tên tệp bắt đầu bằng một dấu chấm, theo sau là bất kỳ ký tự nào ngoại trừ dấu chấm tùy ý theo sau bởi bất kỳ chuỗi nào. Điều này là đủ gần nhưng nó bỏ lỡ các tập tin bắt đầu với hai dấu chấm như thế nào ..foo. Để bao gồm các tệp như vậy, chúng tôi thêm ..?*các tên trùng khớp với các tên tệp bắt đầu bằng hai dấu chấm, theo sau là bất kỳ ký tự nào, tùy ý theo sau bởi bất kỳ chuỗi nào.

Kiểm tra

Bạn có thể kiểm tra các ký tự đại diện này bằng các lệnh bên dưới. Tôi đã thử chúng thành công dưới bash và cá. Họ thất bại dưới 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

0

Bạn cũng có thể tìm và grep với backquote để chọn tệp cho lệnh di chuyển. Truyền những thứ đó vào mv.

Tức là cho các tập tin ẩn

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

Vì thế

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

Di chuyển chỉ các tệp ẩn được chọn vào Bar:

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

Di chuyển tất cả các tệp trong Foo sang Bar kể từ '.' trong lệnh egrep hoạt động như một ký tự đại diện mà không có dấu ngoặc vuông.

Nhân ^vật đảm bảo trận đấu bắt đầu từ đầu dòng.

Một số chi tiết của egrepkhớp mẫu có thể được tìm thấy ở đây .

Sử dụng maxdepth 1dừng tìm thấy từ đi vào thư mục con.


0

Lấy cảm hứng từ câu trả lời này :

Không cần sao chép các tập tin ...

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

Hãy cẩn thận:

  • --link-destđường dẫn phải tuyệt đối hoặc liên quan đến DESTINATION ( Bar trong ví dụ)

  • Bạn đã đặt /sau SOURCE ( Foo/trong ví dụ), nếu không, nó sẽ sao chép thư mục SOURCE thay vì nội dung của nó.


0

Tôi thấy rằng điều này hoạt động tốt cho bash và không cần phải thay đổi tùy chọn shell

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

BIÊN TẬP:

Vì vậy, như G-man đã nói, câu trả lời ban đầu của tôi không tuân thủ theo kiểu posix và khá giống với câu trả lời của ndemou ở trên với một thay đổi, đó là sử dụng mở rộng dấu ngoặc để tạo danh sách các chuỗi được thực hiện. Điều này chỉ có nghĩa là bạn không cần phải cdvào thư mục nguồn. Không phải là một sự thay đổi lớn thực sự, nhưng nó là khác nhau.

Ví dụ: giả sử bạn đã có bố cục sau đây.

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

Câu hỏi ban đầu chỉ đề cập đến các tệp ẩn với một khoảng thời gian duy nhất, nhưng giả sử có một số có hai hoặc nhiều khoảng thời gian khi bắt đầu tên. Bạn chỉ có thể thêm một biểu thức bổ sung vào dấu ngoặc nhọn. Sau đó chúng ta có thể thực thi

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

Điều này được mở rộng đến như sau:

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

Bây giờ bạn sẽ thấy tất cả các tệp nằm trong Destdir :

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

Bạn có thể thực hiện một số điều khá thú vị với niềng răng trong bash với nhiều bổ sung hơn nữa trong 4.x. Kiểm tra bash-hacker cho một số ví dụ tiện lợi.


1
Điều này về cơ bản trùng lặp câu trả lời của ndemou ngoại trừ bạn đã thêm dấu ngoặc nhọn (mà không giải thích chúng). Nhưng (1) câu trả lời của bạn không khớp với các tệp có tên bắt đầu ..và (2) tuân thủ POSIX , bạn nên sử dụng !thay vì ^; ví dụ  {*,.[!.]*}.
G-Man

@ G-Man, xin lỗi về điều đó. Vì vậy, bạn là chính xác và xấu của tôi vì không thấy phản ứng của ndemou. Họ là khá nhiều điều tương tự. 1. cảm ơn bạn đã chỉ ra phiên bản của tôi không tuân thủ posix. 2. Tôi đang sử dụng mở rộng dấu ngoặc để tạo danh sách các chuỗi mà trình bao sau đó sẽ sử dụng để thực hiện một hành động. Điều này cũng có nghĩa là tôi không cần phải cdvào thư mục nguồn để hành động trên các tệp hoặc viết ra cùng một đường dẫn tệp để thể hiện các đường dẫn đến tất cả các tệp. Tôi sẽ cập nhật ví dụ sớm để cung cấp cho bất kỳ độc giả nào hình ảnh rõ hơn về những gì tôi đang nói.
cmndr sp0ck

0

Đối với các bản phân phối Linux tối thiểu , các công việc sau đây phải hoạt động. Đầu tiên, thực hiện di chuyển cơ bản tất cả (mà bỏ lỡ các tệp ẩn). Sau đó, di chuyển tất cả các tệp ẩn (bao gồm cả .và thực sự ..sẽ không được di chuyển).

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

Lưu ý: Nếu không có nội dung hiển thị, lệnh đầu tiên sẽ tạo thông báo lỗi. Lệnh thứ hai sẽ luôn tạo ra lỗi cho biết nó không thể di chuyển .... Như vậy, chỉ cần đưa các lỗi vào /dev/null(như được hiển thị).

Nếu bạn cần cái này như một lớp lót, chỉ cần kết hợp chúng với dấu chấm phẩy:

mv /sourceDir/* /destinationDir 2> /dev/null; mv /sourceDir/.* /destinationDir 2> /dev/null
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.