`Mv ./*` mà không chỉ định đích sẽ làm gì?


27

Tôi vô tình quên xác định đích trước khi nhấn phím Return. Trường hợp mv ./*không xác định đích di chuyển các tập tin và thư mục trong thư mục hiện tại đến?



4
"Tôi ngạc nhiên khi mvchấp nhận 1 đối số" Không. Nó chấp nhận tất cả các đối số shell được truyền cho nó sau khi mở rộng *.
glglgl

Câu trả lời:


41

Nếu đối số cuối cùng là một thư mục, bạn chỉ cần di chuyển tất cả các tệp và thư mục trong thư mục làm việc hiện tại của bạn (ngoại trừ các đối số có tên bắt đầu bằng dấu chấm) vào thư mục đó. Nếu có hai tệp, tệp đầu tiên có thể đã ghi đè lên tệp thứ hai.

Dưới đây là một số minh chứng:

Nhiều hơn hai tệp và đối số cuối cùng là một tệp

$ mkdir d1 d2 d3
$ touch a b c e
$ mv *
mv: target 'e' is not a directory

Nhiều hơn hai tệp và đối số cuối cùng là một thư mục

$ mkdir d1 d2 d3
$ touch a b c
$ mv -v *
'a' -> 'd3/a'
'b' -> 'd3/b'
'c' -> 'd3/c'
'd1' -> 'd3/d1'
'd2' -> 'd3/d2'

Hai tập tin

$ touch a b
$ mv -v *
'a' -> 'b'

Giải thích thêm

Shell mở rộng global ( *) thành các đối số cho mv. Toàn cầu thường được mở rộng theo thứ tự bảng chữ cái. mvluôn luôn nhìn thấy một danh sách các tập tin và thư mục. Nó không bao giờ nhìn thấy toàn cầu.

Lệnh mvhỗ trợ hai loại di chuyển. Một là mv file ... directory. Cái khác là mv old-file-name new-file-name(hoặc mv old-file-name directory/new-file-name).


30

Đầu tiên tôi sẽ tạo một cơ sở thử nghiệm - 5 tệp và một thư mục:

touch file1 file2 file3 file4 file5
mkdir folder

Tiếp theo tôi sẽ chạy một lệnh kiểm tra. Các -vquy định cụ thể tùy chọn mà tôi muốn mọi mệnh lệnh thực thi shell được in tới stderr. Các -xquy định cụ thể tùy chọn mà tôi muốn cùng in để stderr- nhưng tôi muốn nó được thực hiện sau khi lệnh được đánh giá nhưng trước khi vỏ chạy nó.

sh -cxv 'echo mv *'

ĐẦU RA

echo mv *
+ echo mv file1 file2 file3 file4 file5 folder
mv file1 file2 file3 file4 file5 folder

Vì vậy, bạn thấy rằng lệnh tôi cung cấp shell là echo mv *và lệnh shell thực thi sau khi * được mở rộng được echo mvtheo sau bởi tất cả các tệp và thư mục.

Theo mặc định, vỏ sẽ mở rộng những đống như:

sh -cxv 'echo file[1-5]'

ĐẦU RA

echo file[1-5]
+ echo file1 file2 file3 file4 file5
file1 file2 file3 file4 file5

Đây là kết quả của set [+-]fchức năng toàn cầu:

sh -cxvf 'echo file[1-5]'

ĐẦU RA

echo file[1-5]
+ echo 'file[1-5]'
file[1-5]

Vì vậy, khi bạn chạy một lệnh trong shell được cấu hình với các tùy chọn mặc định như mv *shell sẽ mở rộng thành *từ một danh sách đối số của tất cả các tệp trong thư mục hiện tại được sắp xếp theo miền địa phương. Nó thực hiện các tòa nhà exec(ve)cho mv (về cơ bản) với danh sách đối số này được nối thêm. Vì vậy, mvcó được tất cả các đối số như cái vỏ làm cho họ hả hê và sắp xếp chúng. Bên cạnh việc thực hiện straceđể xem các hiệu ứng này, bạn có thể sử dụng gỡ lỗi một lần nữa như:

sh -s -- mv * <<\SCRIPT
sed -n l /proc/$$/cmdline
echo "$@"
SCRIPT

ĐẦU RA

sh\000-s\000--\000mv\000file1\000file2\000file3\000file4\000file5\000folder\
\000$
mv file1 file2 file3 file4 file5 folder

Và di chuyển:

( PS4= IFS=/; set -x mv *; : "/$*/" ) 2>&1

ĐẦU RA

: /mv/file1/file2/file3/file4/file5/folder/

Về cơ bản, shell thực thi mvvới nội dung của thư mục (nếu nó không trống và không bao gồm các tệp / thư mục có tên bắt đầu bằng .) làm danh sách đối số của nó. mvlà POSIX quy định để giải thích lập luận cuối cùng của nó như một thư mục nếu nó được gọi với hơn hai đối số - theo cách tương tự ln(bởi vì, trên thực tế, họ đang công cụ cực kỳ tương tự chức năng cơ bản) .

Đủ echoes mặc dù:

sh -cxv 'mv *' ; ls

ĐẦU RA

mv *
+ mv file1 file2 file3 file4 file5 folder
folder/

Tất cả các tệp đã được chuyển vào đối số cuối cùng - bởi vì đó là một thư mục. Bây giờ nếu nó không phải là một thư mục thì sao?

sh -cxv 'cd *; mv *'; ls . *

ĐẦU RA

cd *; mv *
+ cd folder
+ mv file1 file2 file3 file4 file5
mv: target ‘file5’ is not a directory

.:
folder/

folder:
file1  file2  file3  file4  file5

Đây là cách POSIX chỉ định mv nên hành xử trong trường hợp đó:

mv [-if] source_file target_file

mv [-if] source_file... target_dir

Trong biểu mẫu tóm tắt đầu tiên, mvtiện ích sẽ di chuyển tệp được đặt tên bởi toán hạng source_file đến đích được chỉ định bởi target_file . Biểu mẫu tóm tắt đầu tiên này được giả sử khi toán hạng cuối cùng không đặt tên thư mục hiện có và không phải là một liên kết tượng trưng đề cập đến một thư mục hiện có. Trong trường hợp này, nếu source_file đặt tên tệp không phải là thư mục và target_file kết thúc bằng /slashký tự dấu , mvsẽ coi đây là lỗi và sẽ không có toán hạng source_file nào được xử lý.

Trong biểu mẫu tóm tắt thứ hai, mvsẽ di chuyển từng tệp được đặt tên bởi toán hạng source_file sang tệp đích trong thư mục hiện có được đặt tên bởi toán hạng target_dir hoặc được tham chiếu nếu target_dir là liên kết tượng trưng liên quan đến thư mục hiện có. Đường dẫn đích cho mỗi source_file sẽ là nối của thư mục đích, một /slashký tự nếu mục tiêu không kết thúc bằng a /slashvà thành phần tên đường dẫn cuối cùng của source_file . Dạng thứ hai này được giả sử khi toán hạng cuối cùng đặt tên cho một thư mục hiện có.

Vì vậy, nếu *mở rộng đến:

  • hai tập tin

    • Bạn chỉ nên có một tệp, đó là tệp đầu tiên renamedđến tệp thứ hai sau tệp thứ hai unlinked.
  • một hoặc nhiều tệp được theo sau bởi một thư mục hoặc một liên kết đến một

    • Bạn chỉ nên có một thư mục hoặc một liên kết đến một, đó là nơi tất cả các nội dung trước đó của cha mẹ nó đã được di chuyển.
  • còn gì nữa không

    • Bạn nên có một thông báo lỗi và một tiếng thở dài thỏa mãn.

1
vâng, sử dụng điều cũ sh, tôi quên mất việc gỡ lỗi với điều đó, nhưng liên quan đến câu hỏi ban đầu của tôi, nó có nghĩa là vấn đề là cái vỏ không mv? Điều đó thật khó chịu, nó đơn giản hơn tôi nghĩ ban đầu nhưng đó là một cái bẫy thực sự.
user2485710

5
@ user2485710, điểm mấu chốt là trong Unix, shell chịu trách nhiệm mở rộng các ký tự đại diện trước khi truyền các đối số dòng lệnh cho lệnh. Trong Windows, mỗi lệnh phải tự mở rộng ký tự đại diện. Điều này cho phép hệ vỏ Unix cung cấp các tiện ích ký tự đại diện tiên tiến hoạt động với bất kỳ lệnh nào.
cjm

2
@mikeerv cho rằng thực tế là những tập tin đó không quan trọng lắm. Tôi vội vã dọn dẹp và bỏ qua bước tiếp theo, nhưng tôi có thể xác nhận mọi thứ khi chúng xuất hiện trong bài chỉnh sửa bài đăng / câu hỏi đầu tiên của tôi.
user2485710

6
@mikeerv tl; dr, *không phải là một đối số? Đúng? :)
Bernhard

1
@Bernhard - thực ra, đó là trường hợp tôi không bao gồm - bởi vì nó thể.
mikeerv

18

Đầu tiên, shell mở rộng ./*ra tất cả các tệp trong thư mục hiện tại (ngoại trừ các tệp bắt đầu bằng dấu chấm).

  • nếu không có hoặc chỉ có một tệp: mvthất bại
  • nếu có hai tệp: tệp đầu tiên được chuyển sang tệp thứ hai (do đó bị mất)
  • nếu có nhiều hơn hai tệp:
    • nếu cái cuối cùng là một thư mục: tất cả các tệp được chuyển vào thư mục này
    • nếu không thì mvthất bại

1
Cảm ơn. Làm thế nào để biết thư mục con nào là "cái cuối cùng"?
Tim

7
Chỉ cần gọi echo ./*để xem thứ tự vỏ của bạn sử dụng (thường theo bảng chữ cái).
jofel

Nếu nó thất bại với một tập tin tại sao nó không nói như vậy?
Noumenon

@Noumenon Tôi không hiểu bình luận của bạn. mvtrả về một thông báo lỗi nếu nó được gọi chỉ với một đối số.
jofel

Bạn đúng rồi. Lệnh tương tự từ lịch sử của tôi bây giờ đưa ra missing destination file operand after *filename*mặc dù ngày hôm qua nó đã không.
Noumenon

4

Khi bạn gõ mv ./*, shell của bạn sẽ mở rộng ./*trước khi thực hiện mv.

Một số điều cần lưu ý:

  • Nếu ./*được mở rộng thành ít hơn 2 đối số, mvsẽ, về mặt logic, sẽ tạo ra lỗi.
  • ./* thường sẽ mở rộng vào mọi tệp (bao gồm thư mục) có trong thư mục hiện tại và không bắt đầu bằng dấu chấm.
  • Bạn có thể kiểm soát những gì ./*mở rộng bằng cách đọc tài liệu về trình bao của bạn ( man 7 globlà một điểm vào chủ đề). Vỏ khác nhau sẽ có các tùy chọn khác nhau.

1

Không gì mv *làm gì?

Đây là một câu trả lời ngắn hơn:

Shell mở rộng ký tự đại diện *thành một danh sách các nội dung thư mục. Sau đó, shell chuyển danh sách đầy đủ đó cho lệnh. Lệnh không bao giờ nhìn thấy *.

Lệnh mv file1 file2 ... filen directorysẽ di chuyển file1 ... filen vào thư mục.

Thí dụ

Ở đây tôi tạo một thư mục thử nghiệm chứa ba tệp

$ mkdir t
$ cd t
$ echo a>a; echo b>b; echo c>c
$ ls
a  b  c

Bạn không thể di chuyển nhiều tệp vào một tệp

$ mv *
mv: target `c' is not a directory

Hãy thêm một thư mục con

$ mkdir d

Bạn có thể di chuyển nhiều tệp vào thư mục con

$ mv *
$ ls
d
$ ls d
a  b  c

Lệnh mv file1 file2 ... filen directorynày rất ít có khả năng có bất cứ điều gì để làm với *.
mikeerv

@Mike: Câu trả lời của tôi chỉ ra rằng giai đoạn cuối cùng của việc mở rộng vỏ có hiệu quả biến đổi cái sau thành cái trước. Không biết điều này dường như là gốc rễ của sự nhầm lẫn của OP.
RedGrittyBrick

đúng, nhưng quan điểm của tôi là cái vỏ sẽ không mở rộng *ra file dirtrừ khi có một số địa điểm dtheo sau f.
mikeerv

@mikeerv: Có một miền địa phương như vậy, ví dụ của tôi là cắt và dán từ Putty kết nối với hệ thống GNU / Linux của CentOS 5.6.
RedGrittyBrick

đó là địa phương nào? ví dụ của bạn a b c dlà không file ... dir. Tôi chỉ nhận xét ở tất cả bởi vì bạn không đề cập đến các loại bất cứ nơi nào và nói duy nhất mà *trở nên file ... dirđó không xảy ra.
mikeerv
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.