Duy trì (hoặc khôi phục) quyền của tệp khi thay thế tệp


11

Tôi có một lệnh chấp nhận một tệp làm đối số, sửa đổi tệp, sau đó ghi nó vào tên tệp được chỉ định trong đối số thứ hai. Tôi sẽ gọi chương trình đó modifyfile.

Tôi muốn nó hoạt động "tại chỗ" vì vậy tôi đã viết một tập lệnh shell (bash) để sửa đổi nó thành một tập tin tạm thời sau đó di chuyển nó trở lại:

TMP=`mktemp`
modifyfile "$original" "$TMP"
mv -v "$TMP" "$original"

Điều này có tác dụng phụ đáng tiếc là phá hủy các quyền trên tệp này. Các tập tin được tạo lại với quyền mặc định.

Có cách nào để ra mvlệnh ghi đè đích mà không thay đổi quyền của nó không? Hoặc thay vào đó là cách để lưu người dùng, nhóm và cho phép từ bản gốc và khôi phục chúng?

Câu trả lời:


10

Thay vì sử dụng mv, chỉ cần chuyển hướng cat. Ví dụ:

TMP=$(mktemp)
modifyfile "$original" "$TMP"
cat "$TMP" > "$original"

Điều này ghi đè lên $originalnội dung của $TMP, mà không chạm vào bất cứ điều gì ở cấp độ tập tin.


Không thể có vấn đề nếu một số chương trình có một tệp xử lý mở cho tệp?
Martin von Wittich

2
Sau đó tôi cũng phải rm "$TMP"như vậy, nhưng dường như nó chỉ làm những gì tôi muốn.
Stephen Ostermiller

@MartinvonWittich có lẽ sẽ là một vấn đề nếu bạn đang sử dụng mvthay thế. Tôi không thấy một cách để giải quyết vấn đề đó.
strugee

2
@MartinvonWittich Có. Tạo-mới-sau đó di chuyển cung cấp cho bạn một thay đổi nguyên tử và không ảnh hưởng đến các chương trình mở tệp, nhưng vì nó tạo ra một tệp mới nên quyền sở hữu và quyền của tệp bị mất. Truncate-current-then-write giữ quyền và quyền sở hữu nhưng mất dữ liệu trong trường hợp xảy ra sự cố và quẹt tấm thảm dưới chân các chương trình mở tệp. Bạn không thể kết hợp các phần tốt của cả hai.
Gilles 'SO- ngừng trở nên xấu xa'

1
@MartinvonWittich chownchỉ hoạt động như root. chmodchgrpcó thể hoặc không thể hoạt động tùy thuộc vào quyền của người dùng. Không sao chép các thuộc tính khác như ACL hoặc thuộc tính mở rộng dành riêng cho hệ thống tệp.
Gilles 'SO- ngừng trở nên xấu xa'

10

Có hai chiến lược để thay thế một tệp bằng một phiên bản mới:

  1. Tạo một tệp tạm thời với phiên bản mới, sau đó di chuyển nó vào vị trí.

    • Ưu điểm: nếu một chương trình mở tệp đó, nó sẽ đọc nội dung cũ hoặc nội dung mới, tùy thuộc vào việc nó mở tệp trước hay sau khi di chuyển. Không có trộn lẫn.
    • Ưu điểm: trong trường hợp sự cố, nội dung cũ được bảo tồn.
    • Nhược điểm: do một tệp mới được tạo, các thuộc tính của tệp (quyền sở hữu, quyền, v.v.) không được giữ nguyên.
  2. Ghi đè tập tin cũ vào vị trí.

    • Ưu điểm: thuộc tính của tệp được bảo tồn.
    • Nhược điểm: trong trường hợp có sự cố, tập tin có thể được viết nửa vời.
    • Nhược điểm: nếu một chương trình mở tệp khi nó đang được cập nhật, chương trình này có thể đọc dữ liệu không nhất quán.

Nếu bạn có thể, hãy sử dụng phương thức 1, nhưng trước tiên hãy sao chép các thuộc tính của tệp gốc bằng cp -p --attributes-only. Điều này đòi hỏi GNU coreutils (tức là Linux không nhúng hoặc các môi trường giống như Linux). Nếu bạn cpkhông có --attributes-only, hãy bỏ qua tùy chọn này: nó sẽ hoạt động nhưng nó cũng sẽ sao chép dữ liệu.

tmp=$(mktemp)
cp -p --attributes-only "$original" "$tmp"
modifyfile "$original" "$tmp"
mv -f "$tmp" "$original"

Nếu bạn không thể sao chép các thuộc tính của tệp hiện có, ví dụ vì bạn có quyền ghi trên nó nhưng không sở hữu nó và bạn muốn giữ nguyên chủ sở hữu, thì chỉ có phương pháp 2 là có thể. Để giảm thiểu rủi ro mất dữ liệu:

  • Tạo cửa sổ trong đó tệp sẽ không đầy đủ càng nhỏ càng tốt. Chuẩn bị dữ liệu đầu tiên trong một tệp tạm thời, sau đó sao chép nó vào vị trí.
  • Tạo một bản sao lưu của tập tin cũ đầu tiên.

tmp=$(mktemp)
backup="${original}~"
modifyfile "$original" "$tmp"
cp -p "$original" "$backup"
cp -f "$tmp" "$original"

Câu trả lời tốt đẹp! Ngày nay, tôi khuyên bạn nên sử dụng đối số - chỉ phân phối với lệnh cp trong Phương thức 1 . Theo cách này, cp -p --attributes-only "$original" "$tmp"sẽ không sử dụng tài nguyên để sao chép nội dung của tệp. Tôi không thể tìm thấy từ phiên bản này đối số đã được thêm vào.
Marcelo Barros

@MarceloBarros Nó đã được thêm vào GNU coreutils 8.6 được phát hành 2010-10-15, vì vậy những ngày này nếu bạn có GNU coreutils, bạn nên có nó. Vẫn không có điều đó với các cptriển khai khác .
Gilles 'SO- ngừng trở nên xấu xa'

5

Sau cuộc thảo luận của chúng tôi về câu trả lời đầu tiên, tôi đề xuất một câu trả lời khác:

TMP="$(mktemp "$original".XXXXXXXXXX)"
modifyfile "$original" "$TMP"
chmod --reference="$original" "$TMP"
chown --reference="$original" "$TMP"
mv -f "$TMP" "$original"

Nhận xét:

  • Tôi sử dụng $originaltrong mktempmẫu để đảm bảo rằng tệp tạm thời không được đặt trong /tmpnhưng trong cùng thư mục với $original. Tôi tin rằng nếu /tmpđược gắn trên một hệ thống tập tin khác, hoạt động sẽ không còn là nguyên tử nữa.
  • Kết quả mktempbây giờ được trích dẫn trong trường hợp nó chứa khoảng trắng.
  • Tôi sử dụng $()thay vì `` vì tôi cho rằng nó sạch hơn.
  • ch{mod,own} --referenceđược sử dụng để chuyển các điều khoản của $originalđể $TMP. Nếu ai đó có ý tưởng bổ sung siêu dữ liệu nào có thể và nên được chuyển, thì vui lòng chỉnh sửa bài đăng của tôi và thêm nó.
  • Ồ, điều này đòi hỏi phải có quyền root như Gilles đã chỉ ra. Chà, tôi sẽ không loại bỏ điều này ngay bây giờ khi tôi đã viết nó: P
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.