Di chuyển tệp nhưng chỉ khi nó đóng


10

Tôi muốn di chuyển tệp lớn được tạo bởi quy trình bên ngoài ngay khi đóng.

Lệnh kiểm tra này có đúng không?

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

EDIT: tệp đại diện cho một tập dữ liệu và tôi phải đợi cho đến khi hoàn thành để di chuyển nó để một chương trình khác có thể hành động. Đó là lý do tại sao tôi cần biết nếu quy trình bên ngoài được thực hiện với tệp.


3
Lưu ý bên lề: một khi tệp được mở, các quy trình sử dụng mô tả tệp và dữ liệu inode để thao tác với nó. Thay đổi đường dẫn (nghĩa là di chuyển tệp) sẽ không gây ra quá nhiều rắc rối cho quy trình.
John WH Smith

2
Bạn có kiểm soát quá trình bên ngoài không? Liệu quy trình bên ngoài có thể tạo một tệp tạm thời và đổi tên tệp sau khi viết xong không?
Jenny D

@JennyD Tôi đã làm một số điều tra và hóa ra là sự thật. Tôi không cần lsofchút nào. Tôi chỉ cần kiểm tra xem phần mở rộng tập tin có phải không .tmp. Điều đó làm cho nó tầm thường. Tuy nhiên tôi rất vui khi tôi hỏi câu hỏi của tôi kể từ khi tôi học được một chút thông tin về lsofinotifyvà các công cụ.
Peter Kovac

@PeterKovac Tôi cũng đã học được nhiều hơn về họ, từ việc đọc câu trả lời, vì vậy tôi rất vui vì bạn đã hỏi nó.
Jenny D

@JohnWHSmith - Điều đó thường đúng nếu di chuyển tệp trong cùng một hệ thống tệp, nếu anh ta di chuyển tệp sang hệ thống tệp mới trước khi người viết viết xong, anh ta sẽ mất một số dữ liệu.
Johnny

Câu trả lời:


11

Từ lsoftrang người đàn ông

Lsof trả về một (1) nếu phát hiện bất kỳ lỗi nào, bao gồm cả việc không tìm thấy tên lệnh, tên tệp, địa chỉ Internet hoặc tệp, tên đăng nhập, tệp NFS, PID, PGID hoặc UID mà nó được yêu cầu liệt kê. Nếu tùy chọn -V được chỉ định, lsof sẽ chỉ ra các mục tìm kiếm mà nó không thể liệt kê.

Vì vậy, điều đó sẽ gợi ý rằng lsof failed for some other reasonmệnh đề của bạn sẽ không bao giờ được thực thi.

Bạn đã thử chỉ di chuyển tệp trong khi quá trình bên ngoài của bạn vẫn mở nó? Nếu thư mục đích nằm trên cùng một hệ thống tệp, thì sẽ không có vấn đề gì khi thực hiện điều đó trừ khi bạn cần truy cập nó theo đường dẫn gốc từ quy trình thứ ba vì nút inode bên dưới sẽ giữ nguyên. Nếu không tôi nghĩ mvdù sao cũng sẽ thất bại.

Nếu bạn thực sự cần đợi cho đến khi quá trình bên ngoài của bạn kết thúc với tệp, bạn tốt hơn nên sử dụng một lệnh chặn thay vì bỏ phiếu nhiều lần. Trên Linux, bạn có thể sử dụng inotifywaitcho việc này. Ví dụ:

 inotifywait -e close_write /path/to/file

Nếu bạn phải sử dụng lsof(có thể cho tính di động), bạn có thể thử một cái gì đó như:

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

Cập nhật

Như được lưu ý bởi @JohnWHSmith bên dưới, thiết kế an toàn nhất sẽ luôn sử dụng một lsofvòng lặp như trên vì có thể có nhiều hơn một quy trình sẽ mở tệp để viết (một trường hợp ví dụ có thể là một trình nền lập chỉ mục được viết kém, mở tệp bằng cách đọc / viết cờ khi nó thực sự chỉ nên đọc). inotifywaitVẫn có thể được sử dụng thay vì ngủ, chỉ cần thay thế dòng ngủ bằng inotifywait -e close /path/to/file.


Cảm ơn, tôi đã không nhận thức được inotify. Thật không may, nó không được cài đặt trên hộp của tôi nhưng tôi chắc chắn tôi sẽ tìm thấy một gói ở đâu đó. Xem chỉnh sửa của tôi để biết lý do tại sao tôi cần đóng tệp: đó là tập dữ liệu và nó phải được hoàn thành trước khi xử lý thêm.
Peter Kovac

1
Một lưu ý phụ khác: trong khi inotifywaitsẽ ngăn tập lệnh "bỏ phiếu" thường xuyên, OP vẫn cần kiểm tra lsofmột vòng lặp: nếu tệp được mở hai lần, đóng một lần có thể kích hoạt inotifysự kiện, mặc dù tệp chưa sẵn sàng bị thao túng (ví dụ, trong đoạn mã cuối cùng của bạn, sleepcuộc gọi của bạn có thể được thay thế bằng inotifywait).
John WH Smith

@ John là một close_writeok vì chỉ có một quá trình có thể mở tệp để viết tại một thời điểm. Nó không cho rằng một người khác sẽ không mở nó ngay sau khi nó bị đóng, nhưng sau đó vấn đề tương tự tồn tại với việc lsofbỏ phiếu.
Graeme

1
@Graeme Mặc dù điều này có thể đúng theo thiết kế trong trường hợp của OP, kernel cho phép mở tệp hai lần để ghi (trong trường hợp đó, CLOSE_WRITEđược kích hoạt hai lần).
John WH Smith

@ John, cập nhật.
Graeme

4

Là một cách tiếp cận khác, đây là trường hợp hoàn hảo cho một đường ống - quy trình thứ hai sẽ xử lý đầu ra từ quy trình đầu tiên ngay khi có sẵn, thay vì chờ quá trình hoàn tất:

process1 input_file.dat | process2 > output_file.dat

Ưu điểm:

  • Nhanh hơn nhiều nói chung:
    • Không phải ghi và đọc từ đĩa (điều này có thể tránh được nếu bạn sử dụng ramdisk).
    • Nên sử dụng tài nguyên máy hoàn toàn hơn.
  • Không có tập tin trung gian để loại bỏ sau khi kết thúc.
  • Không cần khóa phức tạp, như trong OP.

Nếu bạn không có cách nào trực tiếp tạo đường ống nhưng bạn có lõi GNU thì bạn có thể sử dụng cách này:

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

Điều này sẽ bắt đầu đọc tệp đầu vào từ đầu, bất kể quá trình đầu tiên là bao xa thông qua việc ghi tệp (ngay cả khi nó chưa bắt đầu hoặc đã kết thúc).


Vâng, đó sẽ là giải pháp "hiển nhiên". Thật không may, quá trình tạo dữ liệu nằm ngoài tầm kiểm soát của tôi (do người dùng khác điều hành).
Peter Kovac

@PeterKovac Điều đó không liên quan: cat input_file.dat |
process2

@MariusMatutiae nhưng catprocess2có thể kết thúc trước khi process1kết thúc. Họ sẽ không chặn.
cpugeniusmv
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.