Bash ย้ายและเปลี่ยนชื่อโดยแยกวิเคราะห์ผลลัพธ์ของ find


3

ฉันมีไดเรกทอรีที่ซ้อนกันของไฟล์ PDF และฉันต้องการแยกออกเป็นไดเรกทอรีระดับที่สูงขึ้นโดยเปลี่ยนชื่อดังนี้:

ไฟล์ของฉันเป็นสิ่งที่ชอบ:

./path1/pathA/fileI.pdf
./path1/pathB/fileII.pdf

ฉันต้องการบรรลุ:

./path1_pathA_fileI.pdf    
./path1_pathB_fileII.pdf

ฉันรู้ว่าฉันสามารถสร้างรายการไฟล์โดยทำ

find . -type f -name "*.pdf"

และฉันสามารถจินตนาการวิธีแก้ปัญหาโดยใช้

find . -type f -name "*.pdf" | mv -t ...

แต่ฉันไม่ทราบวิธีกรอกข้อมูลใน ... เพราะฉันไม่เข้าใจการแยกวิเคราะห์และการมอบหมายตัวแปรในการทุบตี หนึ่งจะแยกเส้นทางที่ '/' และรูปแบบเส้นทางใหม่และชื่อไฟล์ดังกล่าวข้างต้นได้อย่างไร

ขอบคุณมากล่วงหน้า!

คำตอบ:


6

ลอง:

find . -mindepth 2 -type f -name '*.pdf' -exec bash -c 'f=${1#./}; mv "$1" "./${f//\//_}"' None {} \;

วิธีนี้ปลอดภัยสำหรับชื่อไฟล์ทั้งหมดแม้แต่ชื่อที่มีบรรทัดใหม่ในชื่อ

มันทำงานอย่างไร

  • -mindepth 2

    สิ่งนี้บอกว่าจะไม่ประมวลผลไฟล์ใด ๆ ที่มีอยู่แล้วในไดเรกทอรีปัจจุบัน

  • -type f -name '*.pdf'

    นี่เป็นการ จำกัด การค้นหาไฟล์ปกติที่มีpdfนามสกุล

  • -exec bash -c '...' None {} \;

    $1คำสั่งนี้จะทำงานในสายยกจัดหาชื่อไฟล์เป็นอาร์กิวเมนต์แรก

    สำหรับวัตถุประสงค์ของเราสตริงNoneเป็นเพียงตัวยึดตำแหน่ง มันถูกกำหนดให้$0ภายใต้อนุสัญญาทุบตีเป็นชื่อของคำสั่งที่เรากำลังทำงานอยู่

  • f=${1#./}; mv "$1" "./${f//\//_}"

    สิ่งนี้ (a) ลบคำนำหน้า./จากชื่อไฟล์และ (b) ย้ายไฟล์ไปยังตำแหน่งที่ต้องการด้วยชื่อใหม่

    ${1#./}เป็นตัวอย่างของการทุบตีฯกำจัดคำนำหน้า ส่งคืน strign $1โดย./ลบออกจากจุดเริ่มต้น ${f//\//_}เป็นตัวอย่างของการทุบตีฯเปลี่ยนตัวรูปแบบ ก็จะส่งกลับสตริง$fกับทุกแทนที่ด้วย/ _เพื่ออ่านข้อมูลเพิ่มเติมเกี่ยวกับคุณลักษณะเหล่านี้ให้ดูในส่วนman bashสิทธิการขยายตัวพารามิเตอร์

รุ่นที่มีประสิทธิภาพมากขึ้น

เวอร์ชันด้านบนเรียกใช้ bash สำหรับทุกไฟล์ที่พบ หรือเราสามารถเรียกใช้ bash เพียงครั้งเดียวสำหรับไฟล์หลายไฟล์ที่พบ ในการทำเช่นนี้เราได้รวมคำสั่งไว้ในforลูป

find . -mindepth 2 -type f -name '*.pdf' -exec bash -c 'for f in "$@"; do f=${f#./}; echo mv "$f" "./${f//\//_}"; done' None {} +

ปัญหาอื่น

สมมติว่าไฟล์ทั้งหมดที่เราต้องการอยู่ในระดับที่สองไดเรกทอรีและเราต้องการไฟล์ย้ายที่จะมีคำสั่งของชื่อไดเรกทอรีกลับเพื่อให้แทนของเราจบลงด้วย./path1_pathA_fileI.pdf ./pathA_path1_fileI.pdfในกรณีนี้:

for d1 in */; do d1=${d1%/}; for d2 in "$d1"/*/; do d2=${d2%/}; p="${d2#$d1/}_$d1"; for f in "./$d2"/*.pdf; do echo mv "$f" "./${p}_${f#./$d2/}"; done; done; done

หรือสำหรับผู้ที่ต้องการคำสั่งของพวกเขากระจายออกไปหลายบรรทัด:

for d1 in */
do
    d1=${d1%/}
    for d2 in "$d1"/*/
    do
         d2=${d2%/}
         p="${d2#$d1/}_$d1"
         for f in "./$d2"/*.pdf
         do
             echo mv "$f" "./${p}_${f#./$d2/}"
         done
    done
done

ขอบคุณสำหรับการสลายทีละขั้นตอน! จะทำอย่างไรถ้าฉันต้องการเปลี่ยนลำดับของสตริง dir สองสายเพื่อให้เส้นทางสุดท้ายเป็น. /pathA_path1_fileI.pdf ในกรณีนี้การแทนที่รูปแบบ $ {f // \ // _} ไม่ทำงาน สิ่งนี้จะเกิดขึ้นได้อย่างไร?
Canaryyellow

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