วิธีรับไฟล์ N ล่าสุดในไดเรกทอรี?


15

ฉันมีไฟล์หลายไฟล์ที่เรียงลำดับโดยชื่อไฟล์ในไดเรกทอรี ฉันต้องการคัดลอกไฟล์ N สุดท้าย (พูด, N = 4) ไปยังโฮมไดเร็กตอรี่ของฉัน ฉันควรทำอย่างไร

cp ./<the final 4 files> ~/


1
ในคำตอบทั้งหมดด้านล่างคุณอาจต้องเพิ่ม "LC_ALL = ... " หน้าคำสั่งที่ใช้ช่วงเพื่อให้ช่วงของพวกเขาใช้สถานที่ที่เหมาะสมสำหรับคุณ (สถานที่สามารถเปลี่ยนได้และลำดับของตัวละครสามารถเปลี่ยนได้ มากเช่นในบางตำแหน่งที่ตั้ง [az] สามารถมีตัวอักษรทั้งหมดของตัวอักษรยกเว้น "Z" ตามที่ตัวอักษรได้รับคำสั่ง: aAbBcC .... zZ รูปแบบอื่น ๆ มีอยู่ (ตัวอักษรเน้นเสียงและยิ่งแปลกกว่า orderings อื่น ๆ ) ตัวเลือกปกติคือ:LC_ALL=C
Olivier Dulac

คำตอบ:


23

สามารถทำได้อย่างง่ายดายด้วยอาร์เรย์ bash / ksh93 / zsh:

a=(*)
cp -- "${a[@]: -4}" ~/

วิธีนี้ใช้ได้กับชื่อไฟล์ที่ไม่ได้ซ่อนไว้ทั้งหมดแม้ว่าจะมีช่องว่างแท็บบรรทัดใหม่หรืออักขระยากอื่น ๆ (สมมติว่ามีไฟล์ที่ไม่ได้ซ่อนอย่างน้อย 4 ไฟล์ในไดเรกทอรีปัจจุบันด้วยbash)

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

  • a=(*)

    สิ่งนี้จะสร้างอาร์เรย์ที่aมีชื่อไฟล์ทั้งหมด ชื่อไฟล์ที่ส่งคืนโดย bash จะถูกจัดเรียงตามตัวอักษร (ฉันคิดว่านี่คือสิ่งที่คุณหมายถึงโดย"สั่งโดยชื่อไฟล์" )

  • ${a[@]: -4}

    สิ่งนี้จะคืนค่าองค์ประกอบสี่รายการสุดท้ายของอาร์เรย์a(หากอาร์เรย์มีองค์ประกอบอย่างน้อย 4 รายการด้วยbash)

  • cp -- "${a[@]: -4}" ~/

    สิ่งนี้จะคัดลอกชื่อไฟล์สี่ไฟล์สุดท้ายไปยังไดเรกทอรีบ้านของคุณ

หากต้องการคัดลอกและเปลี่ยนชื่อในเวลาเดียวกัน

สิ่งนี้จะคัดลอกสี่ไฟล์ล่าสุดไปยังโฮมไดเร็กตอรี่และในเวลาเดียวกัน, เตรียมสตริงa_เป็นชื่อของไฟล์ที่คัดลอก:

a=(*)
for fname in "${a[@]: -4}"; do cp -- "$fname" ~/a_"$fname"; done

คัดลอกจากไดเรกทอรีอื่นและเปลี่ยนชื่อ

หากเราใช้a=(./some_dir/*)แทนa=(*)เราจะมีปัญหาของไดเรกทอรีที่แนบมากับชื่อไฟล์ ทางออกหนึ่งคือ:

a=(./some_dir/*) 
for f in "${a[@]: -4}"; do cp "$f"  ~/a_"${f##*/}"; done

โซลูชันอื่นคือใช้ subshell และcdไปยังไดเรกทอรีใน subshell:

(cd ./some_dir && a=(*) && for f in "${a[@]: -4}"; do cp -- "$f" ~/a_"$f"; done) 

เมื่อเชลล์ย่อยเสร็จสมบูรณ์เชลล์จะส่งเรากลับไปที่ไดเรกทอรีดั้งเดิม

ตรวจสอบให้แน่ใจว่าการสั่งซื้อสอดคล้องกัน

คำถามจะถามหาไฟล์ "เรียงตามชื่อไฟล์" คำสั่งนั้น Olivier Dulac ชี้ให้เห็นในความคิดเห็นจะแตกต่างจากสถานที่หนึ่งไปยังอีก ถ้ามันเป็นสิ่งสำคัญที่มีผลคงเป็นอิสระจากการตั้งค่าเครื่องแล้วมันจะดีที่สุดที่จะระบุสถานที่เกิดเหตุอย่างชัดเจนเมื่ออาร์เรย์aมีการกำหนด ตัวอย่างเช่น:

LC_ALL=C a=(*)

คุณสามารถค้นหาสถานที่ที่คุณอยู่ในขณะนี้โดยการเรียกใช้localeคำสั่ง


9

หากคุณกำลังใช้zshคุณสามารถใส่วงเล็บใน()รายการที่เรียกว่าตัวระบุแบบกลมซึ่งเลือกไฟล์ที่ต้องการ ในกรณีของคุณที่จะเป็น

cp *(On[1,4]) ~/

ที่นี่จะOnเรียงลำดับชื่อไฟล์ตามลำดับตัวอักษรตามลำดับย้อนหลังและ[1,4]ใช้เวลาเพียง 4 อันดับแรกเท่านั้น

คุณสามารถทำให้มีประสิทธิภาพมากขึ้นโดยการเลือกเฉพาะไฟล์ธรรมดา (ไม่รวมไดเรกทอรีท่อ ฯลฯ ) ด้วย.และยังโดยการผนวก--การcpคำสั่งเพื่อรักษาไฟล์ที่ชื่อขึ้นต้นด้วย-อย่างถูกต้องเพื่อ:

cp -- *(.On[1,4]) ~

เพิ่มDqualifier หากคุณต้องการพิจารณาไฟล์ที่ซ่อนอยู่ (dot-files):

cp -- *(D.On[1,4]) ~

2
หรือ: *([-4,-1]).
Stéphane Chazelas

2
@ StéphaneChazelasใช่ช่วยให้ความกระจ่างสำหรับผู้อ่านในอนาคตที่เรียงลำดับตามตัวอักษรในทิศทางปกติ ( on) เป็นพฤติกรรมเริ่มต้นดังนั้นจึงไม่จำเป็นต้องระบุสิ่งนี้และ-ด้านหน้าของตัวเลขจะนับชื่อไฟล์จากด้านล่าง
jimmij

7

นี่คือวิธีการแก้ปัญหาโดยใช้คำสั่งทุบตีง่ายมาก

find . -maxdepth 1 -type f | sort | tail -n 4 | while read -r file; do cp "$file" ~/; done

คำอธิบาย:

find . -maxdepth 1 -type f

ค้นหาไฟล์ทั้งหมดในไดเรกทอรีปัจจุบัน

sort

เรียงลำดับตัวอักษร

tail -n 4

แสดง 4 บรรทัดสุดท้ายเท่านั้น

while read -r file; do cp "$file" ~/; done

วนซ้ำแต่ละบรรทัดที่ทำการดำเนินการคำสั่งคัดลอก


6

ตราบใดที่คุณพบว่าการเรียงลำดับของเชลล์น่าพอใจคุณสามารถทำได้:

set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
cp "$@" /path/to/target/dir

นี่คล้ายกับbashโซลูชันอาร์เรย์เฉพาะที่เสนอ แต่ควรพกพาไปยังเชลล์ที่รองรับ POSIX หมายเหตุบางประการเกี่ยวกับวิธีการทั้งสอง:

  1. มันเป็นสิ่งสำคัญที่คุณจะนำcpข้อโต้แย้งของคุณด้วยcp --หรือคุณจะได้หนึ่งใน.จุดหรือ/ที่หัวของแต่ละชื่อ หากคุณล้มเหลวในการทำเช่นนี้คุณอาจเสี่ยงต่อการเป็นผู้นำ-ในการcpโต้แย้งแรกซึ่งสามารถตีความได้ว่าเป็นตัวเลือกและทำให้การดำเนินการล้มเหลวหรือแสดงผลลัพธ์ที่ไม่ได้ตั้งใจ

    • แม้ว่าการทำงานกับไดเรกทอรีปัจจุบันเป็นไดเรกทอรีแหล่งที่มานี้จะกระทำได้ง่ายเช่น ... หรือset -- ./*array=(./*)
  2. มันเป็นสิ่งสำคัญเมื่อใช้ทั้งสองวิธีเพื่อให้แน่ใจว่าคุณมีรายการอย่างน้อยมากในอาร์จีอาร์ของคุณในขณะที่คุณพยายามลบ - ฉันทำอย่างนั้นกับการขยายคณิตศาสตร์ สิ่งshiftเดียวที่เกิดขึ้นหากมีอย่างน้อย 4 รายการในอาเรย์อาร์ค - และจากนั้นเลื่อนเฉพาะส่วนแรกที่ทำให้ส่วนเกินของ 4 ..

    • ดังนั้นในset 1 2 3 4 5กรณีที่มันจะเลื่อน1ออกไป แต่ในset 1 2 3กรณีที่จะเปลี่ยนอะไร
    • ตัวอย่างเช่นa=(1 2 3); echo "${a[@]: -4}"พิมพ์บรรทัดว่าง

หากคุณกำลังคัดลอกจากไดเรกทอรีหนึ่งไปยังอีกไดเรกทอรีหนึ่งคุณสามารถใช้pax:

set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
pax -rws '|.*/|new_prefix|' "$@" /path/to/target/dir

... ซึ่งจะใช้การsedแทนที่แบบลักษณะกับชื่อไฟล์ทั้งหมดเมื่อคัดลอก


4

หากมีเพียงไฟล์และชื่อของพวกเขาไม่มีช่องว่างหรือขึ้นบรรทัดใหม่ (และคุณไม่ได้แก้ไข$IFS) หรือตัวอักษร glob (หรือบางตัวอักษรที่ไม่สามารถพิมพ์ได้ด้วยการใช้งานบางอย่างของls) และไม่เริ่มต้นด้วย.คุณสามารถทำเช่นนี้ :

cp -- $(ls | tail -n 4) ~/

นี่เรียกว่าการทดแทนคำสั่งและมันมีประโยชน์จริงๆ tldp.org/LDP/abs/html/commandsub.html#CSPARENS
iyrin

ขอบคุณ! มันได้ผล. มีอยู่a_หรือไม่ที่จะเติมคำนำหน้าให้กับทั้งสี่ไฟล์อย่างรวดเร็วหรือไม่ ฉันพยายามecho "a_$(ls | tail -n 4)"แต่มันจะเสริมไฟล์แรกเท่านั้น
การพนัน Sibbs

@SibbsGambling วิธีการของฉันไม่เหมาะกับที่ แต่คำตอบของ John1024 สามารถปรับให้เข้ากับที่
Hauke ​​Laging

5
ในการแยกวิเคราะห์ทั่วไปlsถือว่าเป็นรูปแบบที่ไม่ดีเพราะโดยทั่วไปแล้วจะล้มเหลวอย่างน่ากลัวในชื่อไฟล์ที่ไม่เพียง แต่มีช่องว่าง แต่ยังแท็บบรรทัดใหม่หรือตัวอักษรอื่น ๆ ที่ถูกต้อง แต่ "ยาก"
HBruijn

2
@HaukeLaging แม้ว่าในปัจจุบันจะไม่มีปัญหา (เพราะฉันไม่มีไฟ "ชื่อ" ซับซ้อนในไดเรกทอรีของฉัน) ฉันอาจมีใน 2 ปีและทันใดนั้นสิ่งที่ตกบนเท้าของฉัน ...
glglgl

1

นี่เป็นวิธีการทุบตีอย่างง่าย ๆ โดยไม่มีรหัสวนซ้ำ คัดลอกไฟล์ 4 ไฟล์ล่าสุดจาก dir ปัจจุบันไปยังปลายทาง:

$ find . -maxdepth 1 -type f |tail -n -4|xargs cp -t "$destdir"

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