ย้ายไฟล์ แต่เมื่อปิดแล้วเท่านั้น


10

ฉันต้องการย้ายไฟล์ขนาดใหญ่ที่สร้างโดยกระบวนการภายนอกทันทีที่ปิด

คำสั่งทดสอบนี้ถูกต้องหรือไม่

if lsof "/file/name"
then
        # file is open, don't touch it!
else
        if [ 1 -eq $? ]
        then
                # file is closed
                mv /file/name /other/file/name
        else
                # lsof failed for some other reason
        fi
fi

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


3
หมายเหตุด้านข้าง: เมื่อเปิดไฟล์แล้วกระบวนการจะใช้ตัวอธิบายไฟล์และข้อมูลไอโหนดเพื่อดำเนินการ การเปลี่ยนเส้นทาง (เช่นย้ายไฟล์) จะไม่ทำให้เกิดปัญหากับกระบวนการมากเกินไป
John WH Smith

2
คุณมีการควบคุมกระบวนการภายนอกหรือไม่? เป็นไปได้ไหมที่กระบวนการภายนอกจะสร้างไฟล์ชั่วคราวและเปลี่ยนชื่อไฟล์เมื่อเขียนเสร็จ
Jenny D

@ JennyD ฉันได้ทำการตรวจสอบบางอย่างและมันกลายเป็นจริง ฉันไม่ต้องการที่ฉันทั้งหมดเพียงต้องตรวจสอบว่านามสกุลของไฟล์ไม่ได้lsof .tmpนั่นทำให้มันเล็กน้อย แต่ฉันดีใจที่ฉันถามคำถามของฉันตั้งแต่ฉันได้เรียนรู้เกี่ยวกับ bit lsofและinotifyและสิ่ง
Peter Kovac

@ PeterKovac ฉันได้เรียนรู้เพิ่มเติมเกี่ยวกับพวกเขาเช่นกันจากการอ่านคำตอบดังนั้นฉันดีใจที่คุณถามมัน
เจนนี่ D

@JohnWHSmith - ปกติแล้วจะเป็นจริงหากย้ายไฟล์ภายในระบบไฟล์เดียวกันหากเขาย้ายไฟล์ไปยังระบบไฟล์ใหม่ก่อนที่ผู้เขียนจะเขียนเสร็จมันจะสูญเสียข้อมูลบางส่วน
Johnny

คำตอบ:


11

จากlsofหน้าคน

Lsof ส่งคืนหนึ่ง (1) หากตรวจพบข้อผิดพลาดใด ๆ รวมถึงความล้มเหลวในการค้นหาชื่อคำสั่งชื่อไฟล์ที่อยู่อินเทอร์เน็ตหรือไฟล์ชื่อเข้าสู่ระบบไฟล์ NFS PIDs PGID หรือ UID ที่ถูกขอให้แสดงรายการ หากระบุตัวเลือก -V lsof จะระบุรายการค้นหาที่ไม่สามารถแสดงรายการได้

ดังนั้นจะแนะนำว่าlsof failed for some other reasonประโยคของคุณจะไม่ถูกดำเนินการ

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

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

 inotifywait -e close_write /path/to/file

หากคุณต้องใช้lsof(อาจจะพกพาได้) คุณสามารถลองดังนี้:

until err_str=$(lsof /path/to/file 2>&1 >/dev/null); do
  if [ -n "$err_str" ]; then
    # lsof printed an error string, file may or may not be open
    echo "lsof: $err_str" >&2

    # tricky to decide what to do here, you may want to retry a number of times,
    # but for this example just break
    break
  fi

  # lsof returned 1 but didn't print an error string, assume the file is open
  sleep 1
done

if [ -z "$err_str" ]; then
  # file has been closed, move it
  mv /path/to/file /destination/path
fi

ปรับปรุง

ตามที่ระบุไว้โดย@JohnWHSด้านล่างการออกแบบที่ปลอดภัยที่สุดจะใช้การlsofวนซ้ำตามที่เป็นไปได้ว่ามีมากกว่าหนึ่งกระบวนการที่จะเปิดไฟล์สำหรับการเขียน (กรณีตัวอย่างอาจเป็น daemon การทำดัชนีที่เขียนไม่ดีซึ่งเปิดไฟล์ด้วยการอ่าน / เขียนการตั้งค่าสถานะเมื่อควรอ่านเท่านั้นจริง ๆ ) inotifywaitยังสามารถนำมาใช้แทนการนอนหลับ inotifywait -e close /path/to/fileแต่เพียงแค่เปลี่ยนสายการนอนหลับที่มี


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

1
หมายเหตุด้านอื่น: ในขณะที่inotifywaitจะป้องกันสคริปต์จาก "การสำรวจ" สองบ่อยครั้งที่ OP ยังคงต้องตรวจสอบlsofในวงวน: หากไฟล์ถูกเปิดสองครั้งการปิดหนึ่งครั้งอาจทำให้เกิดinotifyเหตุการณ์แม้ว่าไฟล์จะไม่พร้อมที่จะ ถูกควบคุม (ตัวอย่างเช่นในโค้ดสุดท้ายของคุณการsleepโทรของคุณอาจถูกแทนที่ด้วยinotifywait)
John WH Smith

@ John a close_writeควรจะใช้ได้เนื่องจากมีเพียงหนึ่งโพรเซสเท่านั้นที่สามารถเปิดไฟล์สำหรับการเขียนในแต่ละครั้ง มันคิดว่าอีกอันหนึ่งจะไม่เปิดขึ้นมาทันทีหลังจากปิด แต่ก็มีปัญหาเดียวกันกับlsofการสำรวจ
แกรม

1
@Graeme แม้ว่าสิ่งนี้อาจเป็นจริงได้ด้วยการออกแบบในกรณีของ OP แต่เคอร์เนลจะอนุญาตให้เปิดไฟล์สองครั้งสำหรับการเขียน (ในกรณีนี้CLOSE_WRITEจะถูกเรียกสองครั้ง)
John WH Smith

@ John อัพเดตแล้ว
แกรม

4

เป็นวิธีทางเลือกนี่เป็นกรณีที่สมบูรณ์แบบสำหรับไพพ์ - กระบวนการที่สองจะประมวลผลเอาต์พุตจากกระบวนการแรกทันทีที่พร้อมใช้งานแทนที่จะรอให้กระบวนการทั้งหมดเสร็จสิ้น:

process1 input_file.dat | process2 > output_file.dat

ข้อดี:

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

หากคุณไม่มีวิธีสร้างท่อโดยตรง แต่คุณมีcoreutils ของ GNUคุณสามารถใช้สิ่งนี้:

tail -F -n +0 input_file.dat | process2 > output_file.dat

สิ่งนี้จะเริ่มอ่านไฟล์อินพุตตั้งแต่เริ่มต้นไม่ว่ากระบวนการแรกจะผ่านการเขียนไฟล์ไปได้ไกลแค่ไหน (แม้ว่าจะยังไม่ได้เริ่มหรือเสร็จสิ้นไปก็ตาม)


ใช่ว่าจะเป็นทางออกที่ "ชัดเจน" น่าเสียดายที่กระบวนการสร้างข้อมูลไม่อยู่ในความควบคุมของฉัน (ดำเนินการโดยผู้ใช้รายอื่น)
Peter Kovac

@PeterKovac นั่นไม่เกี่ยวข้อง: cat input_file.dat |
process2

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