การเปลี่ยนชื่อไฟล์ภาพจำนวนมากด้วยการทุบตี


16

ฉันต้องเปลี่ยนชื่อประมาณ 70,000 ไฟล์ ตัวอย่างเช่น: จากsb_606_HBO_DPM_0089000ถึงsb_606_dpm_0089000ฯลฯ

ช่วงเลขที่ไปจากการ0089000 0163022เป็นเพียงส่วนแรกของชื่อที่ต้องเปลี่ยน ไฟล์ทั้งหมดอยู่ในไดเรกทอรีเดียวและถูกกำหนดหมายเลขตามลำดับ (ลำดับรูปภาพ) ตัวเลขจะต้องไม่เปลี่ยนแปลง

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

แก้ไข:

ฉันพยายามเปลี่ยนชื่อไฟล์ครั้งแรกด้วยmv:

mv sb_606_HBO_DPM_0089000.dpx sb_606_dpm_0089000.dpx

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

mv sb_606_HBO_DPM_0{089000..163023}.dpx sb_606_dpm_0{089000..163023}.dpx

4
สำหรับผู้ตรวจสอบ : ฉันไม่คิดว่านี่เป็นสิ่งซ้ำซ้อน คำตอบของ CLI ส่วนใหญ่ในคำถามอื่น ๆ จะไม่ทำงานที่นี่เพราะไฟล์จำนวนมากชนกับARG_MAXขีด จำกัดของเชลล์ เนื่องจากคำถามนี้ขอคำตอบอย่างชัดเจนสำหรับโซลูชันบรรทัดคำสั่ง (อาจเท่ากัน) โซลูชัน GUI ในคำถามอื่น ๆ ก็ไม่ตรงกัน
ของหวาน

1
ฉันไม่คิดว่านี่เป็นผู้ล่อลวงเพราะมันมีคำถามมากกว่าหนึ่งข้อเกี่ยวกับการเปลี่ยนชื่อไฟล์ โปรดให้ไม่คำถามที่เฉพาะเจาะจงใกล้กับทรัพยากรทั่วไปที่ไม่จริงตอบพวกเขา ...
Zanna

1
@rich หากคุณสามารถแก้ไขได้อย่างชัดเจนว่าคำสั่งใดที่คุณพยายามจะชัดเจนว่านี่ไม่ใช่การหลอกลวง (นี่แสดงให้เราเห็นว่าคุณรู้วิธีนี้) ไชโย
Sparhawk

2
รวยคำถามของคุณไม่ได้ตกเป็นเหยื่อเพราะเป็นคำถามที่เฉพาะเจาะจง ไม่ต้องห่วงเรื่องนั้น ที่สำคัญกว่านั้นหลังจากคำถามได้รับคำตอบจำนวนมากแล้วการแก้ไขอาจไม่ใช่ความคิดที่ดีเพราะการแก้ไขของคุณอาจทำให้คำตอบที่มีอยู่ไม่ถูกต้อง ตอนนี้ฉันรู้สึกว่าคำตอบของฉันควรอธิบายว่าทำไมmv {1..2} {3..4}ไม่ทำงานซึ่งเป็นปัญหาที่แตกต่างจากARG_MAX... ทุกคนที่ตอบอาจจะรู้สึกเหมือนกัน! ดังนั้นจากมุมมองของฉันฉันหวังว่าคุณจะย้อนกลับการแก้ไขครั้งล่าสุดของคุณและถ้าคุณต้องการถามคำถามใหม่ทั้งหมดเกี่ยวกับmvไอเอ็นจีกับช่วง
Zanna

1
@ Parhawk the OP เขียนค่อนข้างชัดเจนจากคำถามรุ่นแรกว่าปัญหาคือargument list too longข้อผิดพลาด ไม่จำเป็นต้องชี้แจงเพิ่มเติมนี่ไม่ใช่การล่อลวงเนื่องจากเราต้องการวิธีแก้ปัญหาสำหรับจัดการกับ ARG_MAX และคำตอบในสำเนาที่เสนอไม่ได้ทำเช่นนั้น
terdon

คำตอบ:


25

วิธีหนึ่งคือใช้findกับ-execและ+ตัวเลือก สิ่งนี้สร้างรายการอาร์กิวเมนต์ แต่แบ่งรายการเป็นการเรียกมากเท่าที่ต้องการเพื่อดำเนินการกับไฟล์ทั้งหมดโดยไม่เกินรายการอาร์กิวเมนต์สูงสุด มันเหมาะเมื่อข้อโต้แย้งทั้งหมดจะได้รับการปฏิบัติเหมือนกัน นี้เป็นกรณีที่มีแต่ไม่ได้อยู่กับrenamemv

คุณอาจต้องติดตั้ง Perl เปลี่ยนชื่อ:

sudo apt install rename

จากนั้นคุณสามารถใช้ตัวอย่างเช่น:

find . -maxdepth 1 -exec rename -n 's/_HBO_DPM_/_dpm_/' {} +

ลบ-nหลังจากการทดสอบเพื่อเปลี่ยนชื่อไฟล์จริง


11

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

MMV

ฉันจะใช้คำสั่ง mmv จากแพ็คเกจชื่อเดียวกัน :

mmv '*HBO_DPM*' '#1dpm#2'

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

mmv 'sb_606_HBO_DPM_*' 'sb_606_dpm_#1'

หากคุณมีไฟล์ที่อยู่นอกช่วงหมายเลขที่ร้องขอในไดเรกทอรีเดียวกันคุณอาจจะดีกว่าถ้ามีลูปมากกว่าตัวเลขที่ให้ไว้ในคำตอบนี้ อย่างไรก็ตามคุณสามารถใช้ลำดับ mmv invocations ที่มีรูปแบบที่เหมาะสม:

mmv 'sb_606_HBO_DPM_0089*'       'sb_606_dpm_0089#1'    # 0089000-0089999
mmv 'sb_606_HBO_DPM_009*'        'sb_606_dpm_009#1'     # 0090000-0099999
mmv 'sb_606_HBO_DPM_01[0-5]*'    'sb_606_dpm_01#1#2'    # 0100000-0159999
mmv 'sb_606_HBO_DPM_016[0-2]*'   'sb_606_dpm_016#1#2'   # 0160000-0162999
mmv 'sb_606_HBO_DPM_01630[01]?'  'sb_606_dpm_01630#1#2' # 0163000-0163019
mmv 'sb_606_HBO_DPM_016302[0-2]' 'sb_606_dpm_016302#1'  # 0163020-0163022

วนรอบตัวเลข

หากคุณต้องการหลีกเลี่ยงการติดตั้งอะไรหรือต้องเลือกตามช่วงตัวเลขหลีกเลี่ยงการจับคู่นอกช่วงนี้และคุณพร้อมที่จะรอการเรียกใช้คำสั่ง 74,023 คุณสามารถใช้ bash loop แบบธรรมดา:

for i in {0089000..0163022}; do mv sb_606_HBO_DPM_$i sb_606_dpm_$i; done

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

for i in {0089000..0163022}; do
  test -e sb_606_HBO_DPM_$i && mv sb_606_HBO_DPM_$i sb_606_dpm_$i
done

โปรดทราบว่าในทางตรงกันข้ามกับfor ((i=89000; i<=163022; ++i))การขยายรั้งจะจัดการกับศูนย์นำเนื่องจาก Bash บางรุ่นวางจำหน่ายเมื่อสองสามปีก่อน การเปลี่ยนแปลงที่ฉันขอจริง ๆ แล้วฉันจึงดีใจที่เห็นกรณีใช้งาน

อ่านเพิ่มเติม: รั้งการขยายตัว{x..y[..incr]}ในหน้าข้อมูลทุบตีโดยเฉพาะอย่างยิ่งส่วนที่เกี่ยวกับ

วนรอบไฟล์

ตัวเลือกอื่นจะวนซ้ำวงกลมที่เหมาะสมแทนที่จะวนซ้ำช่วงของจำนวนเต็มในคำถาม บางสิ่งเช่นนี้

for i in *HBO_DPM*; do mv "$i" "${i/HBO_DPM/dpm}"; done

นี่เป็นการmvเรียกหนึ่งครั้งต่อไฟล์ และอีกครั้งการวนซ้ำอยู่ในรายการองค์ประกอบที่มีความยาว แต่รายการทั้งหมดไม่ถูกส่งผ่านเป็นอาร์กิวเมนต์ไปยังกระบวนการย่อย แต่จัดการภายในด้วยการทุบตีดังนั้นขีด จำกัด จะไม่ทำให้คุณมีปัญหา

อ่านเพิ่มเติม: การขยายพารามิเตอร์เชลล์ในหน้าข้อมูล Bash จัดทำเอกสาร${parameter/pattern/string}หมู่คนอื่น ๆ

หากคุณต้องการ จำกัด ช่วงจำนวนให้กับช่วงที่คุณระบุคุณสามารถเพิ่มการตรวจสอบสำหรับสิ่งต่อไปนี้:

for i in sb_606_HBO_DPM_+([0-9]); do
  if [[ "${i##*_*(0)}" -ge 89000 ]] && [[ "${i##*_*(0)}" -le 163022 ]]; then
    mv "$i" "${i/HBO_DPM/dpm}"
  fi
done

นี่${i##pattern}เอาคำนำหน้าจับคู่ที่ยาวที่สุดจากpattern $iคำนำหน้าที่ยาวที่สุดนั้นถูกกำหนดเป็นสิ่งใดก็ตามจากนั้นขีดเส้นใต้จากนั้นจึงเป็นศูนย์หรือมากกว่าศูนย์ หลังถูกเขียนตาม*(0)ซึ่งเป็นรูปแบบ glob แบบขยายที่ขึ้นอยู่กับextglobตัวเลือกที่ถูกตั้งค่า การลบเลขศูนย์นำหน้าถือเป็นสิ่งสำคัญในการรักษาจำนวนให้เป็นฐาน 10 ไม่ใช่ฐาน 8 +([0-9])อาร์กิวเมนต์ในลูปนั้นเป็นอีกวงกลมหนึ่งที่มีการขยายเพิ่มเติมโดยจับคู่หนึ่งหลักขึ้นไปในกรณีที่คุณมีไฟล์ที่เริ่มต้นเหมือนกัน จำนวน.


ขอขอบคุณ! สิ่งนี้ได้ผลเหมือนฝัน: สำหรับฉันใน {0089000..0163022}; ทำ mv sb_606_HBO_DPM_ $ i sb_606_dpm_ $ i; เสร็จแล้ว - ฉันต้องเพิ่มส่วนขยายชื่อไฟล์เพื่อให้มันใช้งานได้ แต่มันทำตามที่ฉันต้องการและเข้าใจไวยากรณ์ ขอบคุณ @MvG
รวย

@rich: มีความสุขฉันสามารถช่วยคุณและหวังว่าผู้เข้าชมในอนาคตเช่นกัน อย่าลืมยอมรับคำตอบที่มีประโยชน์ที่สุด คุณสามารถเปลี่ยนเครื่องหมายถูกในอนาคตหากมีสิ่งที่ดีกว่าเข้ามา
MvG

10

วิธีหนึ่งในการแก้ไขARG_MAXข้อ จำกัด คือใช้ bash shell ของ builtin printf:

printf '%s\0' sb_* | xargs -0 rename -n 's/HBO_DPM/dpm/'

อดีต

rename -n 's/HBO_DPM/dpm/' sb_*
bash: /usr/bin/rename: Argument list too long

แต่

printf '%s\0' sb_* | xargs -0 rename -n 's/HBO_DPM/dpm/'
rename(sb_606_HBO_DPM_0089000, sb_606_dpm_0089000)
.
.
.
rename(sb_606_HBO_DPM_0163022, sb_606_dpm_0163022)

7
find . -type f -exec bash -c 'echo $1 ${1/HBO_DPM/dpm}' _ {} \;
./sb_606_HBO_DPM_0089000 ./sb_606_dpm_0089000

findในไดเรกทอรีปัจจุบัน.สำหรับไฟล์ทั้งหมด-type fและทำการเปลี่ยนชื่อไฟล์ที่พบว่า$1มีการแทนที่HBO_DPMด้วยdmp หนึ่งโดยหนึ่ง-exec ... \;

แทนที่echoด้วยmvเพื่อดำเนินการเปลี่ยนชื่อ


6

คุณสามารถเขียนสคริปต์หลามเล็ก ๆ น้อย ๆ เช่น:

import os
for file in os.listdir("."):
    os.rename(file, file.replace("HBO_DPM", "dpm"))

บันทึกว่าเป็นไฟล์ข้อความเช่นเดียวกับrename.pyในโฟลเดอร์ไฟล์ที่อยู่ในนั้นกับ terminal ในโฟลเดอร์ที่ไป:

python rename.py

6

คุณสามารถทำไฟล์ตามไฟล์ (อาจใช้เวลาสักครู่) ด้วย

sudo apt install util-linux  # if you don't have it already
for i in *; do rename.ul HBO_DPM dpm "$i"; done

เช่นเดียวกับ Perl ที่renameใช้ในคำตอบอื่น ๆrename.ulนอกจากนี้ยังมีตัวเลือก-nหรือ--no-actสำหรับการทดสอบ


ฉันได้แก้ไขความคิดเห็นของคุณเกี่ยวกับคำตอบของ Zanna โปรดแก้ไขคำตอบของ Zanna หรือแสดงความคิดเห็น
fosslinux

@ubashu ที่ไม่ได้แสดงความคิดเห็นในคำตอบของฉัน - มันก็หมายถึง-nธงที่ผมใช้สำหรับการทดสอบและบอกว่ามันสามารถนำมาใช้ในrename.ulเกินไป
Zanna

3

ฉันเห็นว่าไม่มีใครเชิญเพื่อนที่ดีที่สุดของฉันsedไปงานปาร์ตี้ :) การforวนซ้ำต่อไปนี้จะบรรลุเป้าหมายของคุณ:

for i in sb_606_HBO_DPM*; do
  mv "$i" "$(echo $i | sed 's/HBO_DPM/dpm/')";
done

มีเครื่องมือมากมายสำหรับงานดังกล่าวเลือกเครื่องมือที่เหมาะสมกับคุณมากที่สุด อันนี้ง่ายและเปลี่ยนแปลงได้ง่ายเพื่อให้เหมาะกับวัตถุประสงค์นี้หรืออื่น ๆ ...


ได้รับไม่เกี่ยวข้องมากในกรณีเฉพาะนี้ แต่จะล้มเหลวหากชื่อไฟล์ใด ๆ มีการขึ้นบรรทัดใหม่ ฉันพูดถึงนี้เนื่องจากส่วนใหญ่ (ทั้งหมด?) คำตอบอื่น ๆ ที่มีประสิทธิภาพและสามารถจัดการกับชื่อไฟล์โดยพลการหรือเพียงการทำงานในรูปแบบการตั้งชื่อของ OP ไฟล์
terdon

... การขึ้นบรรทัดใหม่การเว้นวรรคสัญลักษณ์เสริม ... บางอย่างที่สามารถหลีกเลี่ยงได้โดยการอ้างอิง$iในการทดแทนคำสั่ง แต่ไม่มีวิธีง่ายๆในการจัดการบรรทัดใหม่ที่ต่อท้ายในชื่อไฟล์
muru

3

เนื่องจากเราให้ตัวเลือกนี่เป็นวิธี Perl cdลงในไดเร็กทอรีเป้าหมายและรัน:

perl -e 'foreach(glob("sb_*")){rename $_, s/_HBO_DPM_/_dpm_/r}'

คำอธิบาย

  • perl -e: -eเรียกใช้สคริปต์ที่กำหนดโดย
  • foreach(glob){}: การทำงานสิ่งที่อยู่ใน{ }ในแต่ละผลมาจากการ glob
  • glob("sb_*"): กลับรายการของไฟล์และไดเรกทอรีทั้งหมดในไดเรกทอรีปัจจุบันที่มีชื่อตรงกับ sb*glob
  • rename $_, s/_HBO_DPM_/_dpm_/r: เวทมนตร์ perl $_เป็นตัวแปรพิเศษที่เก็บแต่ละองค์ประกอบที่เราทำซ้ำ (ในforeach) ดังนั้นที่นี่จะพบไฟล์แต่ละไฟล์ s/_HBO_DPM_/_dpm_/แทนที่เกิดขึ้นครั้งแรกของการมี_HBO_DPM_ _dpm_มันจะทำงาน$_โดยค่าเริ่มต้นดังนั้นมันจะทำงานในแต่ละชื่อไฟล์ /rหมายถึง "นำไปใช้ทดแทนนี้เพื่อคัดลอกของสตริงเป้าหมาย (ชื่อไฟล์) และกลับสตริงการแก้ไข. renameไม่สิ่งที่คุณคาดหวังว่ามันจะเปลี่ยนชื่อไฟล์ดังนั้นสิ่งที่ทั้งจะเปลี่ยนชื่อชื่อไฟล์ปัจจุบัน (. $_) เพื่อให้ตัวเองด้วยแทนที่ด้วย_HBO_DPM__dpm_

คุณสามารถเขียนสิ่งเดียวกันกับที่ขยาย (และสคริปต์ที่อ่านได้มากขึ้น):

#! /usr/bin/env perl
use strict;
use warnings;

foreach my $fileName (glob("sb_*")){
  ## Copy the name to a new variable
  my $newName = $fileName;
  ## change the copy. $newName is now the changed version
  $newName =~ s/_HBO_DPM_/_dpm_/;
  ## rename
  rename $fileName, $newName;
}

1

ทั้งนี้ขึ้นอยู่กับประเภทของการเปลี่ยนชื่อที่คุณคาดการณ์ไว้การใช้vidir ที่มีการแก้ไขหลายบรรทัดอาจเป็นที่น่าพอใจ
ในกรณีเฉพาะของคุณคุณสามารถเลือกทุกบรรทัดในโปรแกรมแก้ไขข้อความและลบส่วนของชื่อไฟล์"_ HBO"ในการกดปุ่มไม่กี่ครั้ง


ใช่ vi มีค้นหาและแทนที่ gloable
Jasen

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