ฉันไม่ได้หัวใจที่จะทำมันอีกครั้ง แต่ผมเขียนนี้ในการตอบcommandline ค้นหา Sed Exec ที่นั่นผู้ถามต้องการทราบวิธีย้ายทรีทั้งหมดโดยอาจไม่รวมไดเร็กทอรีหรือสองไดเร็กทอรีและเปลี่ยนชื่อไฟล์และไดเร็กทอรีทั้งหมดที่มีสตริง"OLD"เป็น"NEW" แทนแทน
นอกเหนือจากการอธิบายถึงวิธีการใช้คำฟุ่มเฟือยที่ละเอียดอ่อนด้านล่างแล้ววิธีนี้ยังอาจไม่เหมือนใครด้วยการรวมการดีบักในตัว โดยทั่วไปจะไม่ทำอะไรเลยตามที่เขียนไว้ยกเว้นคอมไพล์และบันทึกลงในตัวแปรคำสั่งทั้งหมดที่เชื่อว่าควรทำเพื่อดำเนินการตามที่ร้องขอ
นอกจากนี้ยังหลีกเลี่ยงการวนซ้ำอย่างชัดเจนให้มากที่สุด นอกเหนือจากการsed
ค้นหาซ้ำสำหรับรูปแบบที่ตรงกันมากกว่าหนึ่งรายการแล้วยังไม่มีการเรียกซ้ำแบบอื่นอีกเท่าที่ฉันรู้
และสุดท้ายนี้เป็นnull
ตัวคั่นโดยสิ้นเชิง- จะไม่เดินทางกับอักขระใด ๆ ในชื่อไฟล์ใด ๆ ยกเว้นไฟล์null
. ฉันไม่คิดว่าคุณควรมีแบบนั้น
อย่างไรก็ตามนี่เป็นเรื่องที่รวดเร็วจริงๆ ดู:
% _mvnfind() { mv -n "${1}" "${2}" && cd "${2}"
> read -r SED <<SED
> :;s|${3}\(.*/[^/]*${5}\)|${4}\1|;t;:;s|\(${5}.*\)${3}|\1${4}|;t;s|^[0-9]*[\t]\(mv.*\)${5}|\1|p
> SED
> find . -name "*${3}*" -printf "%d\tmv %P ${5} %P\000" |
> sort -zg | sed -nz ${SED} | read -r ${6}
> echo <<EOF
> Prepared commands saved in variable: ${6}
> To view do: printf ${6} | tr "\000" "\n"
> To run do: sh <<EORUN
> $(printf ${6} | tr "\000" "\n")
> EORUN
> EOF
> }
% rm -rf "${UNNECESSARY:=/any/dirs/you/dont/want/moved}"
% time ( _mvnfind ${SRC=./test_tree} ${TGT=./mv_tree} \
> ${OLD=google} ${NEW=replacement_word} ${sed_sep=SsEeDd} \
> ${sh_io:=sh_io} ; printf %b\\000 "${sh_io}" | tr "\000" "\n" \
> | wc - ; echo ${sh_io} | tr "\000" "\n" | tail -n 2 )
<actual process time used:>
0.06s user 0.03s system 106% cpu 0.090 total
<output from wc:>
Lines Words Bytes
115 362 20691 -
<output from tail:>
mv .config/replacement_word-chrome-beta/Default/.../googlestars \
.config/replacement_word-chrome-beta/Default/.../replacement_wordstars
หมายเหตุ:ข้างต้นfunction
อาจต้องใช้GNU
เวอร์ชันsed
และfind
เพื่อจัดการfind printf
และsed -z -e
และการ:;recursive regex test;t
โทรอย่างถูกต้อง หากสิ่งเหล่านี้ไม่พร้อมใช้งานสำหรับคุณฟังก์ชันนี้อาจซ้ำซ้อนได้โดยมีการปรับเปลี่ยนเล็กน้อยเล็กน้อย
สิ่งนี้ควรทำทุกอย่างที่คุณต้องการตั้งแต่ต้นจนจบด้วยความยุ่งยากเล็กน้อย ฉันทำfork
ด้วยsed
แต่ฉันก็ฝึกsed
เทคนิคการแตกแขนงแบบวนซ้ำดังนั้นฉันจึงมาที่นี่ ฉันเดาว่ามันเหมือนกับการตัดผมลดราคาที่โรงเรียนสอนตัดผม นี่คือขั้นตอนการทำงาน:
rm -rf ${UNNECESSARY}
- ฉันจงใจละทิ้งการเรียกใช้งานที่อาจลบหรือทำลายข้อมูลทุกชนิด คุณพูดถึงสิ่งที่
./app
อาจไม่ต้องการ ลบหรือย้ายไปที่อื่นก่อนหรือคุณอาจสร้าง\( -path PATTERN -exec rm -rf \{\} \)
กิจวัตรประจำวันfind
เพื่อทำแบบเป็นโปรแกรม แต่เป็นของคุณทั้งหมด
_mvnfind "${@}"
- ประกาศอาร์กิวเมนต์และเรียกใช้ฟังก์ชันผู้ปฏิบัติงาน
${sh_io}
มีความสำคัญอย่างยิ่งในการบันทึกผลตอบแทนจากฟังก์ชัน ${sed_sep}
เข้ามาในวินาทีใกล้ นี่คือสตริงโดยพลการที่ใช้อ้างอิงการsed
เรียกซ้ำของฟังก์ชัน หาก${sed_sep}
ถูกตั้งค่าเป็นค่าที่อาจพบได้ในพา ธ หรือชื่อไฟล์ใด ๆ ของคุณที่ดำเนินการ ... เอาล่ะอย่าปล่อยให้เป็น
mv -n $1 $2
- ต้นไม้ทั้งต้นถูกย้ายจากต้น จะช่วยประหยัดอาการปวดหัวได้มาก เชื่อฉัน. ส่วนที่เหลือของสิ่งที่คุณต้องการทำ - การเปลี่ยนชื่อ - เป็นเพียงเรื่องของข้อมูลเมตาของระบบไฟล์ ตัวอย่างเช่นหากคุณย้ายสิ่งนี้จากไดรฟ์หนึ่งไปยังอีกไดรฟ์หนึ่งหรือข้ามขอบเขตของระบบไฟล์ใด ๆ คุณควรทำพร้อมกันด้วยคำสั่งเดียว ยังปลอดภัยกว่าด้วย สังเกต
-noclobber
ชุดตัวเลือกสำหรับmv
; ตามที่เขียนไว้ฟังก์ชันนี้จะไม่ใส่${SRC_DIR}
ตำแหน่งที่มี${TGT_DIR}
อยู่แล้ว
read -R SED <<HEREDOC
- ฉันพบคำสั่งทั้งหมดของ sed ที่นี่เพื่อประหยัดในการหลีกหนีความยุ่งยากและอ่านเป็นตัวแปรเพื่อป้อนข้อมูลด้านล่าง คำอธิบายด้านล่าง
find . -name ${OLD} -printf
- เราเริ่ม
find
กระบวนการ เมื่อfind
เราค้นหาเฉพาะสิ่งที่ต้องการเปลี่ยนชื่อเนื่องจากเราได้ดำเนินการแบบ place-to-place ทั้งหมดmv
ด้วยคำสั่งแรกของฟังก์ชันแล้ว แทนที่จะดำเนินการโดยตรงกับfind
เช่นการexec
โทรเราใช้คำสั่งนี้เพื่อสร้างบรรทัดคำสั่งแบบไดนามิกด้วย-printf
.
%dir-depth :tab: 'mv '%path-to-${SRC}' '${sed_sep}'%path-again :null delimiter:'
- หลังจาก
find
ค้นหาไฟล์แล้วเราต้องการให้สร้างและพิมพ์คำสั่ง( ส่วนใหญ่ ) โดยตรงเราจะต้องดำเนินการเปลี่ยนชื่อของคุณ การ%dir-depth
ตรึงไว้ที่จุดเริ่มต้นของแต่ละบรรทัดจะช่วยให้แน่ใจว่าเราไม่ได้พยายามเปลี่ยนชื่อไฟล์หรือไดเร็กทอรีในแผนผังด้วยออบเจ็กต์หลักที่ยังไม่ได้เปลี่ยนชื่อ find
ใช้เทคนิคการเพิ่มประสิทธิภาพทุกประเภทเพื่อเดินโครงสร้างระบบไฟล์ของคุณและไม่ใช่สิ่งที่แน่นอนว่าจะส่งคืนข้อมูลที่เราต้องการตามลำดับที่ปลอดภัยสำหรับการดำเนินการ นี่คือเหตุผลที่เราต่อไป ...
sort -general-numerical -zero-delimited
- เราจัดเรียง
find
เอาต์พุตทั้งหมดตาม%directory-depth
เพื่อให้เส้นทางที่ใกล้เคียงที่สุดกับ $ {SRC} ทำงานก่อน วิธีนี้จะหลีกเลี่ยงข้อผิดพลาดที่อาจเกิดขึ้นเกี่ยวกับการmv
เข้าไฟล์ไปยังตำแหน่งที่ไม่มีอยู่จริงและช่วยลดความจำเป็นในการวนซ้ำ ( อันที่จริงคุณอาจจะรู้สึกยากที่จะหาลูปเลยก็ได้ )
sed -ex :rcrs;srch|(save${sep}*til)${OLD}|\saved${SUBSTNEW}|;til ${OLD=0}
- ฉันคิดว่านี่เป็นการวนซ้ำเพียงครั้งเดียวในสคริปต์ทั้งหมดและจะวนซ้ำเพียงครั้งที่สองที่
%Path
พิมพ์สำหรับแต่ละสตริงในกรณีที่มีค่ามากกว่าหนึ่ง $ {OLD} ที่อาจต้องแทนที่ วิธีแก้ปัญหาอื่น ๆ ทั้งหมดที่ฉันจินตนาการว่าเกี่ยวข้องกับsed
กระบวนการที่สองและในขณะที่การวนรอบสั้น ๆ อาจไม่เป็นที่ต้องการ แต่แน่นอนว่ามันจะเกิดการวางไข่และทำให้กระบวนการทั้งหมดเกิดขึ้น
- โดยพื้นฐานแล้วสิ่ง
sed
ที่ค้นหาคือ $ {sed_sep} จากนั้นเมื่อพบแล้วให้บันทึกและอักขระทั้งหมดที่พบจนกว่าจะพบ $ {OLD} ซึ่งจะแทนที่ด้วย $ {NEW} จากนั้นกลับไปที่ $ {sed_sep} และค้นหา $ {OLD} อีกครั้งในกรณีที่เกิดขึ้นมากกว่าหนึ่งครั้งในสตริง หากไม่พบระบบจะพิมพ์สตริงที่แก้ไขไปstdout
(ซึ่งจะจับอีกครั้งในลำดับถัดไป) และสิ้นสุดการวนซ้ำ
- วิธีนี้หลีกเลี่ยงการแยกวิเคราะห์สตริงทั้งหมดและทำให้แน่ใจว่าครึ่งแรกของ
mv
สตริงคำสั่งซึ่งต้องรวม $ {OLD} แน่นอนจะรวมไว้ด้วยและครึ่งหลังจะถูกเปลี่ยนแปลงหลาย ๆ ครั้งเท่าที่จำเป็นในการล้าง ชื่อ $ {OLD} จากmv
เส้นทางปลายทางของ
sed -ex...-ex search|%dir_depth(save*)${sed_sep}|(only_saved)|out
- ทั้งสองสายที่นี่เกิดขึ้นโดยที่สอง
-exec
fork
ในครั้งแรกที่เราได้เห็นเราปรับเปลี่ยนmv
คำสั่งเป็นที่จัดทำโดยfind
's -printf
คำสั่งฟังก์ชั่นได้ตามความจำเป็นที่จะต้องปรับเปลี่ยนการอ้างอิงทั้งหมดของ $ {OLD} ไปยัง $ {NEW} แต่เพื่อที่จะทำดังนั้นเราจึงต้องใช้บางส่วน จุดอ้างอิงตามอำเภอใจซึ่งไม่ควรรวมอยู่ในผลลัพธ์สุดท้าย ดังนั้นเมื่อsed
เสร็จสิ้นทุกสิ่งที่ต้องทำเราสั่งให้ลบจุดอ้างอิงออกจากการระงับบัฟเฟอร์ก่อนที่จะส่งต่อไป
และตอนนี้เรากลับมาแล้ว
read
จะได้รับคำสั่งที่มีลักษณะดังนี้:
% mv /path2/$SRC/$OLD_DIR/$OLD_FILE /same/path_w/$NEW_DIR/$NEW_FILE \000
มันจะread
ลงใน${msg}
ฐานะ${sh_io}
ที่สามารถตรวจสอบได้ที่จะออกไปข้างนอกของฟังก์ชัน
เย็น.
- ไมค์