วิธีการแทนที่โฟลเดอร์ที่ชื่อคือวันที่เช่น YYYYMMDD ด้วยลำดับชั้นโฟลเดอร์ของปี, เดือน, วันที่?


8

ฉันมีรายการโฟลเดอร์ที่มีวันที่สำหรับชื่อ วันที่อยู่ในรูปแบบ YYYYMMDD (เช่น 20150129) ภายในโฟลเดอร์เหล่านี้เป็นเอกสารข้อความที่เกี่ยวข้องกับวันที่ที่ระบุ

ฉันต้องการที่จะปรับโครงสร้างพวกเขาในลำดับชั้นของโฟลเดอร์ที่จะไปจากปีที่เดือนถึงวันที่และเพื่อย้ายเอกสารข้อความลงในโฟลเดอร์ 'วันที่' ที่สอดคล้องกันลดลงในลำดับชั้น

กล่าวอีกนัยหนึ่งฉันต้องการให้โฟลเดอร์ 'root' ได้รับการตั้งชื่อหลังจากปีเช่นปี 2015 จากนั้นสร้างโฟลเดอร์ย่อยที่ชื่อเดือนเช่น 01 จากนั้นสร้างโฟลเดอร์ย่อยเพิ่มเติมที่มีวันที่ 29 ซึ่งถือเอกสารข้อความที่เกี่ยวข้อง .

ดังนั้นเส้นทางจะมีลักษณะเหมือนหรือ2015/01/29/file.txt2015>01>29>file.txt

ฉันดู Automator และดูเหมือนว่าบางสิ่งเช่นนี้เป็นไปไม่ได้แม้ว่าฉันจะผิดดังนั้นฉันอยากจะรู้ว่า ...

  1. มีวิธีแก้ปัญหาง่าย ๆ สำหรับคนธรรมดาที่เข้าใจเช่นเวิร์กโฟลว์อัตโนมัติหรือสิ่งนี้ต้องการความเข้าใจเกี่ยวกับคำสั่งเทอร์มินัลและนิพจน์ทั่วไปหรือไม่?

  2. ใครจะแก้ปัญหานี้ได้หากมีวิธีแก้ไข


สำหรับผู้ที่ลงคะแนนให้ปิดคำถามนี้ว่า "กว้างเกินไป" ทำไม ฉันอยากรู้ว่าคำถามนี้กว้างเกินไปหรือไม่
user3439894

โฟลเดอร์ YYYYMMDD เหล่านี้ทั้งหมดอยู่ในโฟลเดอร์หลักเดียวโดยตรงหรือพวกมันกระจายออกไปตามลำดับชั้นที่กว้างขึ้น?
nohillside

@patrix ในกรณีของพวกเขาทั้งหมดในไดเรกทอรีหรือต้นแบบเดียวกันโฟลเดอร์
davidjnatarajan

คำตอบ:


8

สมมติว่าโฟลเดอร์ YYYYMMDD เหล่านี้ทั้งหมดเป็นส่วนหนึ่งของไดเรกทอรีหลักเดียวกันกับที่คุณสามารถเรียกใช้ได้

cd PARENT_DIRECTORY
for d in */; do
    [[ $d =~ [0-9]{8}/ ]] || continue
    mkdir -p -- "${d:0:4}/${d:4:2}"
    mv -- "$d" "${d:0:4}/${d:4:2}/${d:6:2}"
done
  • for d in */; doห่วงอ่านรายการไดเรกทอรีทั้งหมดต่อท้าย/เพื่อให้แน่ใจว่าชื่อไดเรกทอรีเท่านั้นจริงตรง
  • [[ $d =~ [0-9]{8}/ ]] ทดสอบว่ารายการปัจจุบันประกอบด้วยตัวเลข 8 หลักและดำเนินการกับรายการถัดไปหรือไม่หากไม่ใช่
  • ${d:0:4}/${d:4:2}/${d:6:2}ใช้การขยายพารามิเตอร์ภายในbashเพื่อสร้างสตริงที่มีเส้นทางใหม่
  • --ทั้งในmkdirและป้องกันไม่ให้เกิดปัญหาในกรณีที่ไดเรกทอรีหรือชื่อไฟล์เริ่มต้นด้วยmv -สิ่งนี้ไม่สามารถเกิดขึ้นได้ที่นี่ แต่มันอาจเป็นแนวปฏิบัติที่ดีอยู่ดี

ขอบคุณ @terdon และ @ user3439894 สำหรับแนวคิดในการปรับปรุงสคริปต์ต้นฉบับ


ขอบคุณสำหรับคำตอบมันใช้งานได้อย่างสมบูรณ์แบบ! ฉันรู้สึกว่าวิธีนี้ดีกว่าโซลูชันของ @grgarside เร็วกว่าโดยเฉพาะเมื่อต้องรับมือกับคลังข้อมูลขนาดใหญ่รวมถึงเอกสารข้อความนับพัน
davidjnatarajan

8

คุณสามารถใช้สิ่งต่อไปนี้ในเทอร์มินัล cdไปที่โฟลเดอร์ที่มีอยู่จากนั้นเรียกใช้สิ่งต่อไปนี้:

find . -type f -exec bash -c \
  'F=$(sed -E "s#^\./([0-9]{4})([0-9]{2})([0-9]{2})#\1/\2/\3#" <<< $1);\
  mkdir -p -- $(dirname "$F");\
  mv -- "$1" "$F"' - {} \;

find . -type fรับทุกไฟล์ในไดเรกทอรีปัจจุบันซ้ำ
-exec bash -cเปิดเชลล์เพื่อรันคำสั่งต่อไปนี้
F=$(…)เปิด subshell และใช้ sed บนพา ธ ไฟล์เพื่อจัดการพา ธ ไปยังโฟลเดอร์
^\./([0-9]{4})([0-9]{2})([0-9]{2})เป็น regex กับสามกลุ่มจับดังต่อไปนี้ คือการเปลี่ยนที่แต่ละกลุ่มจับ ( ฯลฯ ) จะถูกคั่นด้วย สร้างไดเรกทอรีเพื่อย้ายไฟล์ไปยัง ย้ายแต่ละไฟล์ลงในโฟลเดอร์ที่เกี่ยวข้อง
\1/\2/\3\1/
mkdir -p -- $(dirname "$F")
mv -- "$1" "$F"

นี่จะใช้ลำดับชั้นทางด้านซ้ายและแปลงเป็นลำดับชั้นทางด้านขวา:

├── 20170201               └── 2017
   └── abcdefghij             ├── 02
└── 20170302                      └── 01
    └── abcdefghij 2                  └── abcdefghij
                               └── 03
                                   └── 02
                                       └── abcdefghij 2

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

  'F=$(sed -E "s#^\./([0-9]{4})([0-9]{2})([0-9]{2})(?:/.+)#\1/\2/\3#" <<< $1);\

การ(?:/.+)ทำให้แน่ใจว่าพา ธ มีองค์ประกอบที่ตามมาดังนั้นไม่ต้องสนใจสิ่งใดโดยไม่ต้องมีลูกในไดเรกทอรีหลักซึ่งเป็นไฟล์


@klanomath regex101.com
grg

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