`mv ./* 'โดยไม่ระบุปลายทางทำอะไร


27

ฉันลืมระบุปลายทางโดยไม่ตั้งใจก่อนกดปุ่มย้อนกลับ ที่ไหนmv ./*โดยไม่ระบุปลายทางย้ายไฟล์และไดเรกทอรีภายใต้ไดเรกทอรีปัจจุบันไป?


1
ที่เกี่ยวข้อง: unix.stackexchange.com/questions/79882/…
lgeorget

4
"ฉันประหลาดใจที่mvยอมรับการโต้แย้ง 1 ข้อ" ไม่เช่นนั้น *มันรับทุกข้อโต้แย้งเปลือกผ่านไปหลังจากที่ขยาย
glglgl

คำตอบ:


41

หากอาร์กิวเมนต์สุดท้ายเป็นไดเรกทอรีคุณเพียงแค่ย้ายไฟล์และไดเรกทอรีทั้งหมดในไดเรกทอรีการทำงานปัจจุบันของคุณ (ยกเว้นชื่อที่ขึ้นต้นด้วยจุด) ในไดเรกทอรีนั้น หากมีไฟล์สองไฟล์ไฟล์แรกอาจเขียนทับไฟล์ที่สอง

นี่คือการสาธิต:

มีมากกว่าสองไฟล์และอาร์กิวเมนต์สุดท้ายคือไฟล์

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

มีมากกว่าสองไฟล์และอาร์กิวเมนต์สุดท้ายคือไดเรกทอรี

$ 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'

สองไฟล์

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

คำอธิบายเพิ่มเติม

เปลือกขยาย glob ( *) mvลงในข้อโต้แย้ง glob มักจะขยายตามลำดับตัวอักษร mvเห็นรายการของไฟล์และไดเรกทอรีเสมอ มันไม่เคยเห็นก้อนกลมตัวเอง

คำสั่งmvรองรับการเคลื่อนที่สองประเภท mv file ... directoryหนึ่งในนั้นคือ อีกอันคือmv old-file-name new-file-name(หรือmv old-file-name directory/new-file-name)


30

ก่อนอื่นฉันจะสร้างฐานทดสอบ - 5 ไฟล์และหนึ่งโฟลเดอร์:

touch file1 file2 file3 file4 file5
mkdir folder

ต่อไปฉันจะเรียกใช้คำสั่งทดสอบ ระบุตัวเลือกที่ฉันต้องการคำสั่งรันเปลือกที่จะพิมพ์ทุก-v ตัวเลือกระบุว่าฉันต้องการเดียวกันพิมพ์ไป- แต่ฉันต้องการจะทำหลังจากที่คำสั่งจะถูกประเมิน แต่ก่อนที่จะเปลือกมันวิ่งstderr-xstderr

sh -cxv 'echo mv *'

เอาท์พุท

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

ดังนั้นคุณจะเห็นว่าคำสั่งที่ฉันป้อนเชลล์คือecho mv *และคำสั่งที่เชลล์ดำเนินการหลังจาก *ถูกขยายจะecho mvตามด้วยไฟล์และโฟลเดอร์เหล่านั้นทั้งหมด

โดยค่าเริ่มต้นเปลือกจะขยายglobsเช่น:

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

เอาท์พุท

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

นี่คือผลลัพธ์ของset [+-]fฟังก์ชัน glob:

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

เอาท์พุท

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

ดังนั้นเมื่อคุณรันคำสั่งในเชลล์ที่กำหนดค่าด้วยตัวเลือกเริ่มต้นเช่นmv *เชลล์จะขยายเข้าไปใน*คำว่ารายการอาร์กิวเมนต์ของไฟล์ทั้งหมดในไดเรกทอรีปัจจุบันเรียงตามโลแคล มันเป็น syscall exec(ve)สำหรับmv (เป็นหลัก)กับรายการอาร์กิวเมนต์นี้ต่อท้าย ดังนั้นmvได้รับทั้งหมดของการขัดแย้งเป็นเปลือก globs พวกเขาและพวกเขาเรียงลำดับ นอกเหนือจากการทำstraceเพื่อดูเอฟเฟกต์เหล่านี้คุณสามารถใช้ดีบั๊กได้อีกครั้งเช่น:

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

เอาท์พุท

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

และแบบพกพา:

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

เอาท์พุท

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

โดยทั่วไปเชลล์ดำเนินการmvกับเนื้อหาของไดเรกทอรี(ถ้าไม่ว่างเปล่าและไม่รวมไฟล์ / โฟลเดอร์ที่มีชื่อขึ้นต้นด้วย.)เป็นรายการอาร์กิวเมนต์ mvเป็น POSIX ระบุการตีความอาร์กิวเมนต์สุดท้ายเป็นไดเรกทอรีถ้ามันถูกเรียกด้วยมากกว่าสองข้อโต้แย้ง - ในทางเดียวกันlnคือ(เพราะในความจริงพวกเขากำลังเครื่องมือที่คล้ายกันอย่างไม่น่าเชื่อในฟังก์ชั่นพื้นฐาน)

แต่เพียงพอแล้วecho:

sh -cxv 'mv *' ; ls

เอาท์พุท

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

ไฟล์ทั้งหมดถูกย้ายไปยังอาร์กิวเมนต์สุดท้าย - เนื่องจากเป็นโฟลเดอร์ ตอนนี้ถ้ามันไม่ใช่โฟลเดอร์ล่ะ?

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

เอาท์พุท

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

.:
folder/

folder:
file1  file2  file3  file4  file5

นี่คือวิธีที่POSIX ระบุว่า mvควรประพฤติตนอย่างไรในกรณีนั้น:

mv [-if] source_file target_file

mv [-if] source_file... target_dir

ในรูปแบบบทสรุปแรก, mvยูทิลิตี้จะย้ายไฟล์ตั้งชื่อตามตัวถูกดำเนินการ source_file ไปยังปลายทางที่ระบุโดยtarget_file แบบฟอร์มสรุปครั้งแรกนี้จะถือว่าเมื่อตัวถูกดำเนินการขั้นสุดท้ายไม่ได้ตั้งชื่อไดเรกทอรีที่มีอยู่และไม่ได้เป็นลิงค์สัญลักษณ์ที่อ้างถึงไดเรกทอรีที่มีอยู่ ในกรณีนี้ถ้าsource_fileตั้งชื่อไฟล์ที่ไม่ใช่ไดเรกทอรีและtarget_fileลงท้ายด้วย/slashอักขระต่อท้ายmvจะถือว่าสิ่งนี้เป็นข้อผิดพลาดและไม่มีตัวถูกดำเนินการsource_fileจะถูกประมวลผล

ในรูปแบบสรุปที่สองmvจะย้ายแต่ละไฟล์ที่ชื่อโดย source_file ตัวถูกดำเนินการไปยังไฟล์ปลายทางในไดเรกทอรีที่มีอยู่ชื่อโดยตัวถูกดำเนินการtarget_dirหรืออ้างอิงถ้าtarget_dirเป็นลิงค์สัญลักษณ์ที่อ้างอิงถึงไดเรกทอรีที่มีอยู่ เส้นทางปลายทางสำหรับแต่ละ source_file จะต้องเรียงต่อกันของไดเรกทอรีเป้าหมายเดียว/slashตัวละครถ้าเป้าหมายไม่ได้จบใน/slashและองค์ประกอบชื่อพา ธ สุดท้ายของsource_file แบบฟอร์มที่สองนี้จะถือว่าเมื่อตัวถูกดำเนินการขั้นสุดท้ายตั้งชื่อไดเรกทอรีที่มีอยู่

ดังนั้นหาก*ขยายไปที่:

  • สองไฟล์

    • คุณควรจะมีเพียงหนึ่งไฟล์ซึ่งเป็นครั้งแรกที่สองหลังจากที่สองคือrenamedunlinked
  • ไฟล์อย่างน้อยหนึ่งไฟล์ตามมาด้วยไดเรกทอรีหรือลิงค์ไปยังไฟล์ล่าสุด

    • คุณควรมีเพียงไดเรกทอรีเดียวหรือลิงค์ไปยังไดเรกทอรีเดียวซึ่งเป็นที่ที่เนื้อหาก่อนหน้าของผู้ปกครองทั้งหมดเพิ่งถูกย้าย
  • สิ่งอื่นใด

    • คุณควรมีข้อความแสดงข้อผิดพลาดและถอนหายใจโล่งอกที่น่าพอใจ

1
ใช่ใช้สิ่งเก่าshฉันลืมเกี่ยวกับการแก้จุดบกพร่องด้วย แต่คำถามเดิมของฉันก็หมายความว่าปัญหาเปลือกไม่mv? ช่างเป็นเรื่องที่น่ารังเกียจมันง่ายกว่าที่ข้าเคยคิดไว้ แต่มันเป็นกับดักที่แท้จริง
user2485710

5
@ user2485710 จุดสำคัญคือใน Unix เชลล์มีหน้าที่ขยายสัญลักษณ์แทนก่อนส่งอาร์กิวเมนต์บรรทัดคำสั่งไปยังคำสั่ง ใน Windows แต่ละคำสั่งจะต้องขยายสัญลักษณ์แทนตัวเอง สิ่งนี้อนุญาตให้เชลล์ Unix เสนอสิ่งอำนวยความสะดวกสัญลักษณ์ตัวแทนขั้นสูงที่ทำงานกับคำสั่งใด ๆ
cjm

2
@ mikeserv เนื่องจากความจริงที่ว่าไฟล์เหล่านั้นไม่สำคัญฉันรีบเร่งทำความสะอาดและข้ามไปยังขั้นตอนถัดไป แต่ฉันสามารถยืนยันสิ่งต่าง ๆ ตามที่ปรากฏในการแก้ไขโพสต์ / คำถามแรกของฉัน
2485710

6
@mikeserv tl; dr *ไม่ใช่อาร์กิวเมนต์เดียวใช่ไหม ขวา? :)
แบร์นฮาร์ด

1
@Berhard - อันที่จริงนั่นเป็นกรณีเดียวที่ฉันไม่ครอบคลุม - เพราะอาจเป็นได้
mikeserv

18

ก่อนอื่นเชลล์จะขยาย./*ไปยังไฟล์ทั้งหมดในไดเรกทอรีปัจจุบัน (ยกเว้นไฟล์ที่ขึ้นต้นด้วยจุด)

  • หากไม่มีไฟล์ใดไฟล์หนึ่งหรือเพียงไฟล์เดียว: mvล้มเหลว
  • หากมีสองไฟล์: ไฟล์แรกจะถูกย้ายไปยังไฟล์ที่สอง (ซึ่งจะหายไป)
  • หากมีมากกว่าสองไฟล์:
    • หากไฟล์สุดท้ายเป็นไดเร็กทอรี: ไฟล์ทั้งหมดจะถูกย้ายไปยังไดเร็กทอรีนี้
    • มิฉะนั้นจะmvล้มเหลว

1
ขอบคุณ จะบอกได้อย่างไรว่าไดเรกทอรีย่อยใด "อันสุดท้าย"
ทิม

7
เพียงแค่โทรecho ./*ดูว่าลำดับที่เชลล์ของคุณใช้ (ปกติคือตัวอักษร)
jofel

ถ้ามันล้มเหลวด้วยไฟล์เดียวทำไมถึงไม่พูดอย่างนั้น?
Noumenon

@Noumenon ฉันไม่เข้าใจความคิดเห็นของคุณ mvส่งกลับข้อผิดพลาดถ้ามันถูกเรียกว่ามีเพียงหนึ่งข้อโต้แย้ง
jofel

คุณพูดถูก คำสั่งเดียวกันจากประวัติของฉันตอนนี้ให้missing destination file operand after *filename*แม้ว่าจะไม่ได้
Noumenon

4

เมื่อคุณพิมพ์mv ./*, เปลือกของคุณจะขยายตัวก่อนที่จะดำเนิน./*mv

สิ่งที่ควรทราบ:

  • หาก./*มีการขยายออกเป็นอาร์กิวเมนต์น้อยกว่า 2 ข้อmvจะทำให้เกิดข้อผิดพลาดทางตรรกะ
  • ./* โดยปกติจะขยายเป็นทุก ๆ ไฟล์ (รวมถึงไดเรกทอรี) ที่มีอยู่ในไดเรกทอรีปัจจุบันและไม่เริ่มต้นด้วยจุด
  • คุณสามารถควบคุมสิ่งที่./*ขยายออกไปได้โดยอ่านเอกสารของเชลล์ของคุณ ( man 7 globเป็นจุดเริ่มต้นของหัวข้อ) กระสุนที่แตกต่างกันจะมีตัวเลือกต่างกัน

1

อะไรmv *ทำอย่างไร

นี่คือคำตอบที่สั้นกว่า:

เชลล์ขยาย wildcard *เป็นรายการของเนื้อหาไดเร็กทอรี จากนั้นเชลล์จะส่งรายการทั้งหมดไปยังคำสั่ง *คำสั่งที่ไม่เคยเห็น

คำสั่งmv file1 file2 ... filen directoryจะย้าย file1 ... filen ไปยังไดเรกทอรี

ตัวอย่าง

ที่นี่ฉันทำไดเรกทอรีทดสอบที่มีสามไฟล์

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

คุณไม่สามารถย้ายไฟล์หลายไฟล์เป็นไฟล์เดียว

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

ลองเพิ่มไดเรกทอรีย่อย

$ mkdir d

คุณสามารถย้ายไฟล์หลายไฟล์ไปไว้ในส่วนย่อยได้

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

คำสั่งmv file1 file2 ... filen directoryมีโอกาสน้อยมากที่จะทำอะไรกับ*มัน
mikeserv

@ ไมค์: คำตอบของฉันชี้ให้เห็นว่าขั้นตอนสุดท้ายของการขยายตัวของเชลล์เปลี่ยนได้อย่างมีประสิทธิภาพหลังในอดีต ไม่รู้ว่านี่เป็นรากเหง้าของความสับสนของ OP
RedGrittyBrick

ใช่ แต่จุดของฉันคือเปลือกจะไม่ขยาย*เข้าไปfile dirจนกว่าจะมีสถานที่บางส่วนดังนี้d f
mikeserv

@mikeserv: มีสถานที่ดังกล่าวตัวอย่างของฉันคือการตัดและวางจากการเชื่อมต่อกับระบบ PutOS CentOS 5.6 GNU / Linux
RedGrittyBrick

สถานที่ใด ตัวอย่างของคุณa b c dไม่ตรงfile ... dirไหน ฉันแสดงความคิดเห็นเพียงเพราะคุณไม่ได้พูดถึงการเรียงลำดับที่ใดก็ได้และพูดเฉพาะที่*กลายเป็นfile ... dirสิ่งที่ไม่เกิดขึ้น
mikeserv
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.