มีวิธีทำให้ mv สร้างไดเรกทอรีที่จะย้ายไปหากไม่มีหรือไม่?


275

ดังนั้นถ้าฉันอยู่ในโฮมไดเร็กตอรี่ของฉันและฉันต้องการย้าย foo.c ไปที่ ~ / bar / baz / foo.c, แต่ไดเรกทอรีเหล่านั้นไม่มีอยู่, มีวิธีที่จะสร้างไดเร็กตอรี่เหล่านั้นโดยอัตโนมัติ, คุณจะต้องพิมพ์

mv foo.c ~/bar/baz/ 

และทุกอย่างจะทำงานออกมา? ดูเหมือนว่าคุณสามารถนามแฝง mv เป็นสคริปต์ทุบตีง่าย ๆ ที่จะตรวจสอบว่ามีไดเรกทอรีเหล่านั้นอยู่หรือไม่ถ้าไม่เรียก mkdir แล้ว mv แต่ฉันคิดว่าฉันจะตรวจสอบดูว่าใครมีความคิดที่ดีกว่า


ที่เกี่ยวข้อง: stackoverflow.com/questions/1529946/…
kvantour

คำตอบ:


294

วิธีการเกี่ยวกับหนึ่งซับ (ในทุบตี):

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

ทำลายมันลง:

mkdir --parents ./some/path

สร้างไดเรกทอรี (รวมถึงไดเรกทอรีระดับกลางทั้งหมด) หลังจากนั้น:

mv yourfile.txt $_

ย้ายไฟล์ไปยังไดเรกทอรีนั้น ($ _ ขยายไปยังอาร์กิวเมนต์สุดท้ายที่ส่งผ่านไปยังคำสั่งเชลล์ก่อนหน้าเช่น: ไดเรกทอรีที่สร้างขึ้นใหม่)

ฉันไม่แน่ใจว่ามันจะใช้งานกระสุนปืนอื่นได้ไกลแค่ไหน แต่มันอาจทำให้คุณมีความคิดเกี่ยวกับสิ่งที่จะมองหา

นี่คือตัวอย่างการใช้เทคนิคนี้:

$ > ls
$ > touch yourfile.txt
$ > ls
yourfile.txt
$ > mkdir --parents ./some/path/; mv yourfile.txt $_
$ > ls -F
some/
$ > ls some/path/
yourfile.txt


1
ทำไมความลุ่มหลงกับซ้ำซ้อน./ทุกที่? args ไม่ใช่คำสั่งใน PATH หากไม่มีไดเรคทอรี....
Jens

3
สำหรับมือใหม่ - ผู้ปกครองสามารถย่อให้เหลือ -p
sakurashinken

8
อย่างผิดปกติ--parentsไม่สามารถใช้ได้กับ macOS 10.13.2 -pคุณจำเป็นต้องใช้
Joshua Pinter

1
ใช้งานไม่ได้หากย้ายไฟล์เช่น. / some/path/foo ในกรณีนี้คำสั่งควรเป็น: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

2
ฉันเป็นคนเดียวเท่านั้นที่รู้รหัสนี้ไม่สมเหตุสมผล? คุณสร้างเส้นทางสัมบูรณ์ซ้ำไปยังไฟล์ที่มีอยู่ (ซึ่งหมายความว่าเส้นทางนี้มีอยู่แล้ว) จากนั้นย้ายไฟล์ไปยังตำแหน่ง (ซึ่งในตัวอย่างของคุณไม่มีอยู่)
vdegenne

1
@ user544262772 GNU coreutils ' dirnameไม่จำเป็นต้องมีอาร์กิวเมนต์อยู่ แต่เป็นการจัดการสตริงที่บริสุทธิ์ ไม่สามารถพูดถึงการแจกแจงอื่น ๆ ได้ ไม่มีอะไรผิดปกติกับรหัสนี้
TroyHurts

ฉันคิดว่านี่เป็นคำตอบที่ง่ายที่สุดอัตโนมัติ for f in *.txt; do mkdir -p `dirname /destination/${f//regex/repl}`; mv "$f" "/destination/${f//regex/repl}; done
Kyle

23

บันทึกเป็นสคริปต์ชื่อ mv หรือ mv.sh

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

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

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

สิ่งเหล่านี้ขึ้นอยู่กับการส่งของ Chris Lutz


3
นี่เป็นการตอบคำถามที่ถูกต้องที่สุดของ IMHO ผู้ใช้ต้องการmvคำสั่งขั้นสูง ประโยชน์อย่างยิ่งสำหรับการเขียนสคริปต์คือคุณไม่จำเป็นต้องการใช้ตรวจสอบและการทำงานตลอดเวลาที่คุณจำเป็นต้องใช้mkdir -p mvแต่เนื่องจากฉันต้องการพฤติกรรมข้อผิดพลาดเริ่มต้นสำหรับmvฉันจึงเปลี่ยนชื่อฟังก์ชั่นเป็นmvp- เพื่อให้ฉันรู้ว่าเมื่อฉันจะสร้างไดเรกทอรี
Brian Duncan

ดูเหมือนว่าความคิดทางออกที่ดีที่สุด แต่การใช้งานล้มเหลวอย่างมาก
Ulises Layera

2
@UlisesLayera ฉันแก้ไขอัลกอริทึมเพื่อให้มีประสิทธิภาพมากขึ้น มันไม่ควรผิดพลาดตอนนี้
Sepero

ใครช่วยกรุณาอธิบายความหมายของ[ ! -a "$dir" ]ฉันได้ทำการทดลองกับครึ่งขวาเป็นจริงและเท็จทั้งสองประเมินเป็นจริง .. ??? เพื่อประโยชน์ของผู้อื่น tmp = "$ {tmp: -1}" ดูเหมือนจะคว้าตัวละครตัวสุดท้ายของไฟล์เพื่อให้แน่ใจว่ามันไม่ใช่เส้นทาง (/)
bazz

@bazz มันควรจะทำ "ถ้า $ dir ไม่มีอยู่แล้วทำต่อไป" น่าเสียดายที่คุณถูกต้องมีข้อผิดพลาดเมื่อใช้ "! -a" และฉันไม่รู้ว่าทำไม ฉันได้แก้ไขโค้ดนิดหน่อยและควรจะใช้งานได้แล้ว
Sepero

13

คุณสามารถใช้mkdir:

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

สคริปต์ง่าย ๆ ที่จะทำโดยอัตโนมัติ (ยังไม่ทดลอง):

#!/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 "$@"

เมื่อฉันเรียกใช้ "$ dirname ~? bar / baz /" ฉันจะได้รับ "/ home / dmckee / bar" ซึ่งไม่ใช่สิ่งที่คุณต้องการที่นี่ ...
dmckee --- ผู้ดูแลอดีตลูกแมว

@dmckee อ่าคุณพูดถูก ความคิดเกี่ยวกับวิธีการแก้ปัญหานี้? หากคุณป้อน ~ / bar / baz คุณ (a) ต้องการคัดลอกไปที่ ~ / bar / และเปลี่ยนชื่อเป็น baz หรือ (b) คัดลอกเป็น ~ / bar / baz / เครื่องมือที่ดีกว่าสำหรับงานคืออะไร?
แปลกหน้า

@dmckee ฉันใช้ regexp / sed เพื่อหาวิธีแก้ปัญหา มันเหมาะกับความชอบของคุณหรือไม่? =]
แปลกหน้า

ดียกเว้น $ 2 ไม่ใช่อาร์กิวเมนต์สุดท้ายเว้นแต่ $ # = 2 ฉันมีโปรแกรม la ที่พิมพ์อาร์กิวเมนต์ล่าสุด ฉันเคยใช้มันในเวอร์ชันของคำสั่ง cp (เพื่อเพิ่มไดเรกทอรีปัจจุบันหากอาร์กิวเมนต์สุดท้ายไม่ใช่ไดเรกทอรี) แม้แต่กระสุนทันสมัยสนับสนุน $ 1 .. $ 9 เท่านั้น; สคริปต์ Perl อาจดีกว่า
Jonathan Leffler

@Leffler ไม่เป็นความจริง - ฉันรู้ว่า bash รองรับอาร์กิวเมนต์มากกว่า 9 รายการ (ใช้ $ {123} เป็นวิธีหนึ่ง) ฉันไม่รู้ Perl ดังนั้นอย่าลังเลที่จะตอบคำถามด้วยตัวเอง =]
แปลกหน้า

7

ดูเหมือนว่าคำตอบคือไม่ :) ฉันไม่ต้องการสร้างนามแฝงหรือ func เพียงแค่ทำสิ่งนี้บ่อยครั้งเพราะมันเป็นครั้งเดียวและฉันกำลังพิมพ์mvคำสั่งอยู่แล้ว แต่ฉันพบบางสิ่งที่ทำงานได้ดีสำหรับ:

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

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


5

เชลล์สคริปต์ต่อไปนี้อาจจะ?

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

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


1
ด้วยรหัสของคุณการย้ายจะไม่ดำเนินการหากไม่มีไดเรกทอรีอยู่!
แปลกหน้า

1
และคุณพลาดแฟล็ก "-p" ไปที่ mkdir
dmckee --- ผู้ดูแลอดีตลูกแมว

หากไม่มีไดเรกทอรีอยู่ให้สร้างมันหากคุณต้องการย้ายมัน (แฟล็ก -p คงที่)
Chris Lutz

ฉันหมายถึงเมื่อคุณเรียกใช้สคริปต์และไดเรกทอรี $ 2 ไม่มีอยู่มันถูกสร้างขึ้น แต่ไฟล์จะไม่คัดลอก
แปลกหน้า

เขาให้โครงสร้างที่เรียบง่ายและเพิ่มข้อมูลเกี่ยวกับวิธีการขยาย ลงคะแนนทำไม!
ypnos

5

คำสั่งrsyncสามารถทำเคล็ดลับได้เฉพาะในกรณีที่ไม่มีไดเรกทอรีสุดท้ายในเส้นทางปลายทางเช่นสำหรับเส้นทางปลายทาง~/bar/baz/หากbarมีอยู่ แต่bazไม่มีอยู่สามารถใช้คำสั่งต่อไปนี้ได้:

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)

ในกรณีนี้bazไดเรกทอรีจะถูกสร้างขึ้นหากไม่มีอยู่ แต่ถ้าทั้งคู่barและbazไม่มี rsync จะล้มเหลว:

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]

ดังนั้นโดยทั่วไปก็ควรจะปลอดภัยที่จะใช้เป็นชื่อแทนสำหรับrsync -av --remove-source-filesmv


4

วิธีที่ง่ายที่สุดที่จะทำคือ:

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

สมมติว่าฉันดาวน์โหลดไฟล์ pdf ที่อยู่ในไดเรกทอรีดาวน์โหลด ( ~/download) และฉันต้องการย้ายไฟล์ทั้งหมดไปยังไดเรกทอรีที่ไม่มีอยู่ (สมมุติว่าmy_PDF)

ฉันจะพิมพ์คำสั่งต่อไปนี้ (ทำให้แน่ใจว่าไดเรกทอรีการทำงานปัจจุบันของฉัน~/download)

mkdir my_PDF && mv *.pdf $_

คุณสามารถเพิ่ม-pตัวเลือกmkdirหากคุณต้องการสร้างไดเรกทอรีย่อยเช่นนี้: (ฉันควรจะสร้างชื่อไดเรกทอรีย่อยpython):

mkdir -p my_PDF/python && mv *.pdf $_

2

Sillier แต่วิธีการทำงาน:

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

สร้างไดเร็กทอรีด้วย mkdir -p รวมถึงไดเร็กทอรีชั่วคราวที่แบ่งใช้ชื่อไฟล์ปลายทางจากนั้นลบไดเร็กทอรีชื่อไฟล์นั้นด้วย rmdir แบบง่ายจากนั้นย้ายไฟล์ของคุณไปยังปลายทางใหม่ ฉันคิดว่าคำตอบที่ใช้ dirname น่าจะดีที่สุด


Yepp, ค่อนข้างงี่เง่า :-) ถ้า $ 2 เป็นไดเร็กตอรี่ (เหมือนในคำถามเดิม) แล้วมันจะล้มเหลวอย่างน่าสังเวชเนื่องจากไดเรกทอรีสุดท้ายจะถูกลบดังนั้น 'mv' จะล้มเหลว และถ้ามี $ 2 อยู่แล้วก็จะพยายามลบออก
Tylla

ขอบคุณ! นี่คือสิ่งที่ฉันต้องการเพื่อให้บรรลุ: mv ./old ./subdir/new โดยที่ subdir ยังไม่มีอยู่
Mike Murray


1

รหัส:

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

ตัวอย่าง:

อาร์กิวเมนต์: "d1" "d2 / sub"

mkdir: created directory 'd2'
renamed 'd1' -> 'd2/sub'

1

นี้จะย้ายfoo.cไปยังไดเรกทอรีใหม่กับไดเรกทอรีแม่bazbar

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

-pตัวเลือกในการmkdirจะสร้างไดเรกทอรีกลางตามความจำเป็น
หากไม่มี-pไดเรกทอรีทั้งหมดในคำนำหน้าเส้นทางต้องมีอยู่แล้ว

ทุกอย่างภายใน backticks ``จะถูกดำเนินการและเอาท์พุทจะถูกส่งกลับในบรรทัดเป็นส่วนหนึ่งของคำสั่งของคุณ
เนื่องจากmkdirไม่ได้ส่งคืนอะไรecho $_จะมีการเพิ่มเฉพาะเอาต์พุตของคำสั่ง

$_อ้างอิงอาร์กิวเมนต์สุดท้ายกับคำสั่งที่เรียกใช้ก่อนหน้านี้
ในกรณีนี้มันจะกลับเส้นทางไปยังไดเรกทอรีใหม่ของคุณ ( ~/bar/baz/) ผ่านไปยังmkdirคำสั่ง


ฉันซิปที่เก็บโดยไม่ให้ปลายทางและต้องการที่จะย้ายไฟล์ทั้งหมดยกเว้นจากไดเรกทอรีปัจจุบันของฉันไปยังไดเรกทอรีใหม่ที่เรียกว่าdemo-app.zip บรรทัดต่อไปนี้ทำการหลอกลวง:demo-app

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

ls -Aส่งคืนชื่อไฟล์ทั้งหมดรวมถึงไฟล์ที่ซ่อนอยู่ ( ยกเว้นนัย.และ.. )

สัญลักษณ์ไพพ์|ใช้เพื่อไพพ์เอาต์พุตของlsคำสั่งถึงgrep( ยูทิลิตีการค้นหาบรรทัดคำสั่งแบบข้อความธรรมดา ) ธงนำในการค้นหาและกลับชื่อไฟล์ทั้งหมดไม่รวม รายชื่อของไฟล์ที่ถูกเพิ่มเข้าไปในบรรทัดคำสั่งของเราเป็นข้อโต้แย้งที่มากับคำสั่งย้าย อาร์กิวเมนต์เป้าหมายคือเส้นทางไปยังไดเรกทอรีใหม่ผ่านไปอ้างอิงใช้และการใช้เอาท์พุท
-vgrepdemo-app.zip
mvmkdir$_echo


1
นี่คือรายละเอียดและคำอธิบายในระดับดีโดยเฉพาะอย่างยิ่งสำหรับการโพสต์แรก ขอบคุณ!
Jeremy Caney

ขอบคุณ @JeremyCaney! ฉันตื่นเต้นที่จะเริ่มช่วยเหลือ!
Evan Wunder

0

คุณสามารถใช้ส่วนขยายรั้ง:

mkdir -p directory{1..3}/subdirectory{1..3}/subsubdirectory{1..2}      
  • ซึ่งสร้าง 3 ไดเรกทอรี (directory1, directory2, directory3)
    • และในแต่ละหนึ่งในสองไดเรกทอรีย่อย (ไดเรกทอรีย่อย 1, ไดเรกทอรีย่อย 2)
      • และในแต่ละรายการย่อยสองไดเรกทอรีย่อย (subsubdirectory1 และ subsubdirectory2)

คุณต้องใช้ bash 3.0 หรือใหม่กว่า


5
น่าสนใจ แต่ไม่ตอบคำถามของ OP
Alexey Feldgendler

0
$what=/path/to/file;
$dest=/dest/path;

mkdir -p "$(dirname "$dest")";
mv "$what" "$dest"

1
เพื่อให้เป็นสคริปต์แบบสแตนด์อโลนเพียงแค่แทนที่ $ dest และ $ src ด้วย $ 1 และ $ 2
cab404


0

คุณสามารถโยงคำสั่งmkdir ~/bar/baz | mv foo.c ~/bar/baz/
หรือสคริปต์เชลล์อยู่ที่นี่:

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

1. เปิดตัวแก้ไขข้อความใด ๆ
2.สคริปต์เชลล์คัดลอกวาง
3. บันทึกเป็นmkdir_mv.sh
4. ป้อนไดเรกทอรีของสคริปต์ของคุณ: cd your/script/directory
5. เปลี่ยนโหมดไฟล์ : chmod +x mkdir_mv.sh
6. ตั้งค่านามแฝง : alias mv="./mkdir_mv.sh"
ตอนนี้ทุกครั้งที่คุณเรียกใช้mvไดเรกทอรีย้ายคำสั่งจะถูกสร้างขึ้นหากไม่มีอยู่


0

จากความคิดเห็นในคำตอบอื่นนี่คือฟังก์ชันเชลล์ของฉัน

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

รวมสิ่งนี้ใน. bashrc หรือคล้ายกันเพื่อให้คุณสามารถใช้ได้ทุกที่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.