Làm cách nào để kiểm tra xem một tập tin có thể được tạo hoặc cắt / ghi đè trong bash không?


15

Người dùng gọi tập lệnh của tôi bằng một đường dẫn tệp sẽ được tạo hoặc ghi đè tại một số điểm trong tập lệnh, như foo.sh file.txthoặc foo.sh dir/file.txt.

Hành vi tạo hoặc ghi đè giống như các yêu cầu đặt tệp ở bên phải của >toán tử chuyển hướng đầu ra hoặc chuyển nó dưới dạng đối số tee(thực tế, chuyển nó làm đối số cho teechính xác những gì tôi đang làm ).

Trước khi tôi đi vào can đảm của tập lệnh, tôi muốn kiểm tra hợp lý nếu tập tin có thể được tạo / ghi đè, nhưng không thực sự tạo ra nó. Kiểm tra này không phải là hoàn hảo, và vâng tôi nhận ra rằng tình huống có thể thay đổi giữa kiểm tra và điểm mà tập tin thực sự được viết - nhưng ở đây tôi ổn với giải pháp loại nỗ lực tốt nhất để tôi có thể bảo lãnh sớm trong trường hợp đường dẫn tệp không hợp lệ.

Ví dụ về lý do tệp không thể được tạo:

  • tập tin chứa một thành phần thư mục, giống như dir/file.txtnhưng thư mục dirkhông tồn tại
  • người dùng không có quyền ghi trong thư mục được chỉ định (hoặc CWD nếu không có thư mục nào được chỉ định

Có, tôi nhận ra rằng việc kiểm tra quyền "lên phía trước" không phải là UNIX Way ™ , thay vào đó tôi chỉ nên thử thao tác và yêu cầu sự tha thứ sau đó. Tuy nhiên, trong kịch bản cụ thể của tôi, điều này dẫn đến trải nghiệm người dùng xấu và tôi không thể thay đổi thành phần chịu trách nhiệm.


Đối số sẽ luôn dựa trên thư mục hiện tại hay người dùng có thể chỉ định một đường dẫn đầy đủ không?
Jesse_b

@Jesse_b - Tôi cho rằng người dùng có thể chỉ định một đường dẫn tuyệt đối như thế nào /foo/bar/file.txt. Về cơ bản, tôi chuyển đường dẫn đến nơi teegiống như được truyền vào dòng lệnh. Điều đó sẽ "chỉ hoạt động" với cả hai đường dẫn tuyệt đối và tương đối, phải không? tee $OUT_FILEOUT_FILE
BeeOnRope

@BeeOnRope, tee -- "$OUT_FILE"ít nhất bạn không cần . Điều gì xảy ra nếu tệp đã tồn tại hoặc tồn tại nhưng không phải là tệp thông thường (thư mục, symlink, fifo)?
Stéphane Chazelas

@ StéphaneChazelas - tôi cũng đang sử dụng tee "${OUT_FILE}.tmp". Nếu tệp đã tồn tại, teeghi đè, đó là hành vi mong muốn trong trường hợp này. Nếu đó là một thư mục, teesẽ thất bại (tôi nghĩ). symlink Tôi không chắc chắn 100%?
BeeOnRope

Câu trả lời:


11

Bài kiểm tra rõ ràng sẽ là:

if touch /path/to/file; then
    : it can be created
fi

Nhưng nó thực sự tạo tập tin nếu nó chưa có ở đó. Chúng ta có thể tự dọn dẹp:

if touch /path/to/file; then
    rm /path/to/file
fi

Nhưng điều này sẽ xóa một tập tin đã tồn tại, mà bạn có thể không muốn.

Chúng tôi, tuy nhiên, có một cách xung quanh này:

if mkdir /path/to/file; then
    rmdir /path/to/file
fi

Bạn không thể có một thư mục có cùng tên với một đối tượng khác trong thư mục đó. Tôi không thể nghĩ ra một tình huống trong đó bạn có thể tạo một thư mục nhưng không tạo được một tập tin. Sau thử nghiệm này, tập lệnh của bạn sẽ được tự do tạo một quy tắc thông thường /path/to/filevà làm bất cứ điều gì nó làm hài lòng với nó.


2
Điều đó rất gọn gàng xử lý rất nhiều vấn đề! Một nhận xét mã giải thích và biện minh rằng giải pháp bất thường có thể vượt quá độ dài câu trả lời của bạn. :-)
jpaugh

Tôi cũng đã sử dụng if mkdir /var/run/lockdirnhư một bài kiểm tra khóa. Đây là nguyên tử, trong khi if ! [[ -f /var/run/lockfile ]]; then touch /var/run/lockfilecó một lát thời gian nhỏ giữa thử nghiệm và touchnơi một trường hợp khác của tập lệnh có thể bắt đầu.
DopeGhoti

8

Từ những gì tôi đang thu thập, bạn muốn kiểm tra xem khi sử dụng

tee -- "$OUT_FILE"

(lưu ý --hoặc nó sẽ không hoạt động đối với tên tệp bắt đầu bằng -), teesẽ thành công khi mở tệp để viết.

Vậy thôi đo:

  • độ dài của đường dẫn tệp không vượt quá giới hạn PATH_MAX
  • tệp tồn tại (sau khi phân giải symlink) và không thuộc loại thư mục và bạn có quyền ghi vào nó.
  • nếu tệp không tồn tại, tên thư mục của tệp tồn tại (sau độ phân giải symlink) dưới dạng thư mục và bạn có quyền ghi và tìm kiếm cho nó và độ dài tên tệp không vượt quá giới hạn NAME_MAX của hệ thống tệp mà thư mục nằm trong đó.
  • hoặc tệp là một liên kết tượng trưng chỉ đến một tệp không tồn tại và không phải là một vòng lặp liên kết tượng trưng nhưng đáp ứng các tiêu chí ở trên

Bây giờ chúng ta sẽ bỏ qua các hệ thống tệp như vfat, ntfs hoặc hfsplus có các hạn chế về tên tệp giá trị byte nào có thể chứa, hạn ngạch đĩa, giới hạn quy trình, selinux, apparmor hoặc cơ chế bảo mật khác theo cách, hệ thống tệp đầy đủ, không còn inode, thiết bị các tệp không thể được mở theo cách này vì lý do này hay lý do khác, các tệp có thể thực thi hiện được ánh xạ trong một số không gian địa chỉ quy trình, tất cả cũng có thể ảnh hưởng đến khả năng mở hoặc tạo tệp.

Với zsh:

zmodload zsh/system
tee_would_likely_succeed() {
  local file=$1 ERRNO=0 LC_ALL=C
  if [ -d "$file" ]; then
    return 1 # directory
  elif [ -w "$file" ]; then
    return 0 # writable non-directory
  elif [ -e "$file" ]; then
    return 1 # exists, non-writable
  elif [ "$errnos[ERRNO]" != ENOENT ]; then
    return 1 # only ENOENT error can be recovered
  else
    local dir=$file:P:h base=$file:t
    [ -d "$dir" ] &&    # directory
      [ -w "$dir" ] &&  # writable
      [ -x "$dir" ] &&  # and searchable
      (($#base <= $(getconf -- NAME_MAX "$dir")))
    return
  fi
}

Trong bashhoặc bất kỳ vỏ giống như Bourne, chỉ cần thay thế

zmodload zsh/system
tee_would_likely_succeed() {
  <zsh-code>
}

với:

tee_would_likely_succeed() {
  zsh -s -- "$@" << 'EOF'
  zmodload zsh/system
  <zsh-code>
EOF
}

Các zshtính năng cụ thể ở đây là $ERRNO(hiển thị mã lỗi của lệnh gọi hệ thống cuối cùng) và $errnos[]mảng kết hợp để dịch sang các tên macro C tiêu chuẩn tương ứng. Và $var:h(từ csh) và $var:P(cần zsh 5.3 trở lên).

Bash không có tính năng tương đương nào.

$file:hcó thể được thay thế bằng dir=$(dirname -- "$file"; echo .); dir=${dir%??}hoặc bằng GNU dirname: IFS= read -rd '' dir < <(dirname -z -- "$file").

Đối với $errnos[ERRNO] == ENOENT, một cách tiếp cận có thể là chạy ls -Ldtrên tệp và kiểm tra xem thông báo lỗi có tương ứng với lỗi ENOENT hay không. Làm điều đó đáng tin cậy và di chuyển là khó khăn mặc dù.

Một cách tiếp cận có thể là:

msg_for_ENOENT=$(LC_ALL=C ls -d -- '/no such file' 2>&1)
msg_for_ENOENT=${msg_for_ENOENT##*:}

(giả sử rằng thông báo lỗi kết thúc bằng syserror()bản dịch ENOENTvà bản dịch đó không bao gồm a :) và sau đó, thay vì [ -e "$file" ], làm:

err=$(ls -Ld -- "$file" 2>&1)

Và kiểm tra lỗi ENOENT với

case $err in
  (*:"$msg_for_ENOENT") ...
esac

Phần $file:Pnày là khó nhất để đạt được bash, đặc biệt là trên FreeBSD.

FreeBSD có một realpathlệnh và một readlinklệnh chấp nhận một -ftùy chọn, nhưng chúng không thể được sử dụng trong trường hợp tệp là một liên kết tượng trưng không giải quyết. Đó là cùng với perl's Cwd::realpath().

pythonChúng os.path.realpath()dường như hoạt động tương tự như vậy zsh $file:P, vì vậy, giả sử rằng ít nhất một phiên bản đã pythonđược cài đặt và có một pythonlệnh liên quan đến một trong số chúng (không phải là một quy định trên FreeBSD), bạn có thể làm:

dir=$(python -c '
import os, sys
print(os.path.realpath(sys.argv[1]) + ".")' "$dir") || return
dir=${dir%.}

Nhưng sau đó, bạn cũng có thể làm toàn bộ trong python.

Hoặc bạn có thể quyết định không xử lý tất cả các trường hợp góc.


Vâng, bạn hiểu ý định của tôi một cách chính xác. Trên thực tế, câu hỏi ban đầu không rõ ràng: Ban đầu tôi đã nói về việc tạo tệp, nhưng thực sự tôi đang sử dụng nó teevì vậy yêu cầu thực sự là tệp có thể được tạo nếu nó không tồn tại hoặc có thể bị cắt ngắn về 0 và bị ghi đè nếu nó làm (hoặc tuy nhiên khác teexử lý đó).
BeeOnRope

Có vẻ như lần chỉnh sửa cuối cùng của bạn đã xóa định dạng
D. Ben Knoble

Cảm ơn, @ giám mục, @ D.BenKnoble, xem chỉnh sửa.
Stéphane Chazelas

1
@DennisWilliamson, vâng, shell là một công cụ để gọi các chương trình và mọi chương trình mà bạn gọi trong tập lệnh của bạn phải được cài đặt để tập lệnh của bạn hoạt động, không cần phải nói. macOS đi kèm với cả bash và zsh được cài đặt theo mặc định. FreeBSD không đi kèm. OP có thể có một lý do chính đáng để muốn làm điều đó trong bash, có thể đó là thứ họ muốn thêm vào một tập lệnh bash đã được viết.
Stéphane Chazelas

1
@pipe, một lần nữa, shell là trình thông dịch lệnh. Bạn sẽ thấy rằng nhiều trích đoạn mã shell viết ở đây invoke dịch viên của các ngôn ngữ khác như perl, awk, sedhoặc python(hoặc thậm chí sh, bash...). Shell scripting là về việc sử dụng lệnh tốt nhất cho nhiệm vụ. Ở đây thậm chí perlkhông có một tương đương với zsh's $var:P(nó Cwd::realpathcư xử như BSD realpathhoặc readlink -fkhi bạn muốn nó để hành xử như GNU readlink -fhoặc python' s os.path.realpath())
Stéphane Chazelas

6

Một tùy chọn bạn có thể muốn xem xét là tạo tệp sớm nhưng chỉ điền vào sau trong tập lệnh của bạn. Bạn có thể sử dụng execlệnh để mở tệp trong bộ mô tả tệp (chẳng hạn như 3, 4, v.v.) và sau đó sử dụng chuyển hướng đến bộ mô tả tệp ( >&3, v.v.) để ghi nội dung vào tệp đó.

Cái gì đó như:

#!/bin/bash

# Open the file for read/write, so it doesn't get
# truncated just yet (to preserve the contents in
# case the initial checks fail.)
exec 3<>dir/file.txt || {
    echo "Error creating dir/file.txt" >&2
    exit 1
}

# Long checks here...
check_ok || {
    echo "Failed checks" >&2
    # cleanup file before bailing out
    rm -f dir/file.txt
    exit 1
}

# We're ready to write, first truncate the file.
# Use "truncate(1)" from coreutils, and pass it
# /dev/fd/3 so the file doesn't need to be reopened.
truncate -s 0 /dev/fd/3

# Now populate the file, use a redirection to write
# to the previously opened file descriptor.
populate_contents >&3

Bạn cũng có thể sử dụng a trapđể dọn sạch tệp bị lỗi, đó là một thực tế phổ biến.

Bằng cách này, bạn sẽ có được một kiểm tra thực sự về các quyền mà bạn sẽ có thể tạo tệp, đồng thời có thể thực hiện nó đủ sớm để nếu điều đó không thành công bạn đã dành thời gian chờ đợi để kiểm tra lâu.


CẬP NHẬT: Để tránh ghi đè tệp trong trường hợp kiểm tra không thành công, hãy sử dụng fd<>filechuyển hướng của bash mà không cắt bớt tệp ngay lập tức. (Chúng tôi không quan tâm đến việc đọc từ tệp, đây chỉ là một cách giải quyết nên chúng tôi không cắt bớt nó. Việc đăng ký >>có lẽ cũng sẽ hiệu quả, nhưng tôi có xu hướng tìm thấy điều này thanh lịch hơn một chút, giữ cờ O_APPEND của bức tranh.)

Khi chúng tôi sẵn sàng thay thế nội dung, trước tiên chúng tôi cần cắt bớt tệp (nếu không, nếu chúng tôi viết ít byte hơn trong tệp trước đó, thì các byte theo dõi sẽ ở đó.) Chúng tôi có thể sử dụng cắt ngắn (1) lệnh từ coreutils cho mục đích đó và chúng ta có thể truyền cho nó bộ mô tả tệp mở mà chúng ta có (sử dụng /dev/fd/3tệp giả) để không cần mở lại tệp. (Một lần nữa, về mặt kỹ thuật, một cái gì đó đơn giản hơn : >dir/file.txtcó thể sẽ hoạt động, nhưng không phải mở lại tệp là một giải pháp thanh lịch hơn.)


Tôi đã xem xét vấn đề này, nhưng vấn đề là nếu một lỗi vô tội khác xảy ra ở đâu đó giữa khi tôi tạo tệp và thời điểm sau đó tôi sẽ ghi vào đó, người dùng có thể sẽ buồn khi phát hiện ra rằng tệp được chỉ định đã bị ghi đè .
BeeOnRope

1
@BeeOnRope một tùy chọn khác sẽ không ghi đè tệp là cố gắng không thêm vào nó: echo -n >>filehoặc true >> file. Tất nhiên, ext4 có các tệp chỉ nối thêm, nhưng bạn có thể sống với dương tính giả đó.
mosvy

1
@BeeOnRope Nếu bạn cần giữ lại (a) tệp hiện có hoặc (b) tệp mới (hoàn chỉnh), đó là một câu hỏi khác với những gì bạn đã hỏi. Một câu trả lời hay cho nó là chuyển tập tin hiện có sang một tên mới (ví dụ my-file.txt.backup) trước khi tạo tập tin mới. Một giải pháp khác là ghi tệp mới vào một tệp tạm thời trong cùng một thư mục, sau đó sao chép nó qua tệp cũ sau khi phần còn lại của tập lệnh thành công --- nếu thao tác cuối cùng thất bại, người dùng có thể tự khắc phục sự cố mà không bị mất sự tiến bộ của anh ấy hoặc cô ấy.
jpaugh

1
@BeeOnRope Nếu bạn đi theo lộ trình "thay thế nguyên tử" (đây là một tuyến tốt!) Thì thông thường bạn sẽ muốn ghi tệp tạm thời vào cùng thư mục với tệp cuối cùng (chúng cần phải nằm trong cùng hệ thống tệp để đổi tên (2) để thành công) và sử dụng một tên duy nhất (vì vậy nếu có hai phiên bản tập lệnh đang chạy, chúng sẽ không ghi đè lên các tệp tạm thời của nhau.) Mở một tệp tạm thời để ghi trong cùng thư mục thường là một dấu hiệu tốt cho bạn 'sẽ có thể đổi tên nó sau (tốt, ngoại trừ nếu mục tiêu cuối cùng tồn tại và là một thư mục), do đó, ở một mức độ nào đó, nó cũng giải quyết câu hỏi của bạn.
filbranden

1
@jpaugh - Tôi khá miễn cưỡng khi làm điều đó. Nó chắc chắn sẽ dẫn đến câu trả lời cố gắng giải quyết vấn đề cấp cao hơn của tôi, điều này có thể thừa nhận các giải pháp không liên quan đến câu hỏi "Làm thế nào tôi có thể kiểm tra ...". Bất kể vấn đề cấp cao của tôi là gì, tôi nghĩ rằng câu hỏi này chỉ có một mình là điều bạn có thể muốn làm một cách hợp lý. Sẽ là không công bằng cho những người đang trả lời câu hỏi cụ thể đó nếu tôi thay đổi nó thành "giải quyết vấn đề cụ thể này mà tôi gặp phải với kịch bản của mình". Tôi đã bao gồm những chi tiết đó ở đây vì tôi đã được hỏi, nhưng tôi không muốn thay đổi câu hỏi chính.
BeeOnRope

4

Tôi nghĩ giải pháp của DopeGhoti là tốt hơn nhưng điều này cũng sẽ hiệu quả:

file=$1
if [[ "${file:0:1}" == '/' ]]; then
    dir=${file%/*}
elif [[ "$file" =~ .*/.* ]]; then
    dir="$(PWD)/${file%/*}"
else
    dir=$(PWD)
fi

if [[ -w "$dir" ]]; then
    echo "writable"
    #do stuff with writable file
else
    echo "not writable"
    #do stuff without writable file
fi

Đầu tiên if xây dựng kiểm tra nếu đối số là một đường dẫn đầy đủ (bắt đầu bằng /) và đặt dirbiến cho đường dẫn thư mục lên đến cuối cùng /. Mặt khác, nếu đối số không bắt đầu bằng một /nhưng có chứa /(chỉ định thư mục con) thì nó sẽ được đặt thành dirthư mục làm việc hiện tại + đường dẫn thư mục con. Nếu không, nó giả sử thư mục làm việc hiện tại. Sau đó nó kiểm tra xem thư mục đó có thể ghi được không.


4

Điều gì về việc sử dụng testlệnh bình thường như được nêu dưới đây?

FILE=$1

DIR=$(dirname $FILE) # $DIR now contains '.' for file names only, 'foo' for 'foo/bar'

if [ -d $DIR ] ; then
  echo "base directory $DIR for file exists"
  if [ -e $FILE ] ; then
    if [ -w $FILE ] ; then
      echo "file exists, is writeable"
    else
      echo "file exists, NOT writeable"
    fi
  elif [ -w $DIR ] ; then
    echo "directory is writeable"
  else
    echo "directory is NOT writeable"
  fi
else
  echo "can NOT create file in non-existent directory $DIR "
fi

Tôi gần như trùng lặp câu trả lời này! Giới thiệu hay, nhưng bạn có thể đề cập man test, và điều đó [ ]tương đương với kiểm tra (không phải kiến ​​thức phổ biến nữa). Đây là một lớp lót tôi sắp sử dụng trong câu trả lời thấp kém của mình, để bao quát trường hợp chính xác, cho hậu thế:if [ -w $1] && [ ! -d $1 ] ; then echo "do stuff"; fi
Iron Gremlin

4

Bạn đã đề cập đến trải nghiệm người dùng đang lái câu hỏi của bạn. Tôi sẽ trả lời từ góc độ UX, vì bạn đã có câu trả lời tốt về mặt kỹ thuật.

Thay vì thực hiện kiểm tra trước, làm thế nào về việc ghi kết quả vào một tệp tạm thời , cuối cùng, đặt kết quả vào tệp mong muốn của người dùng? Giống:

userfile=${1:?Where would you like the file written?}
tmpfile=$(mktemp)

# ... all the complicated stuff, writing into "${tmpfile}"

# fill user's file, keeping existing permissions or creating anew
# while respecting umask
cat "${tmpfile}" > "${userfile}"
if [ 0 -eq $? ]; then
    rm "${tmpfile}"
else
    echo "Couldn't write results into ${userfile}." >&2
    echo "Results available in ${tmpfile}." >&2
    exit 1
fi

Cách tốt với cách tiếp cận này: nó tạo ra hoạt động mong muốn trong kịch bản đường dẫn hạnh phúc thông thường, giải quyết vấn đề nguyên tử thử nghiệm và thiết lập, bảo toàn quyền của tệp mục tiêu trong khi tạo nếu cần và rất đơn giản để thực hiện.

Lưu ý: chúng tôi đã sử dụng mv, chúng tôi sẽ giữ quyền của tệp tạm thời - chúng tôi không muốn điều đó, tôi nghĩ: chúng tôi muốn giữ quyền như được đặt trên tệp đích.

Bây giờ, điều xấu: nó đòi hỏi gấp đôi không gian ( cat .. >xây dựng), buộc người dùng phải thực hiện một số công việc thủ công nếu tệp mục tiêu không thể ghi vào thời điểm cần thiết và để lại tệp tạm thời (có thể có bảo mật hoặc vấn đề bảo trì).


Trong thực tế, điều này ít nhiều là những gì tôi đang làm bây giờ. Tôi viết hầu hết các kết quả vào một tệp tạm thời và cuối cùng thực hiện bước xử lý cuối cùng và viết kết quả vào tệp cuối cùng. Vấn đề là tôi muốn bảo lãnh sớm (khi bắt đầu kịch bản) nếu bước cuối cùng đó có khả năng thất bại. Kịch bản có thể chạy không giám sát trong vài phút hoặc vài giờ, vì vậy bạn thực sự muốn biết trước rằng nó sẽ thất bại!
BeeOnRope

Chắc chắn, nhưng có rất nhiều cách có thể thất bại: đĩa có thể lấp đầy, thư mục ngược dòng có thể bị xóa, quyền có thể thay đổi, tệp mục tiêu có thể được sử dụng cho một số nội dung quan trọng khác và người dùng quên rằng anh ta đã gán tệp đó bị hủy bởi hoạt động. Nếu chúng ta nói về điều này từ góc độ UX thuần túy, thì có lẽ điều đúng đắn là coi nó như một công việc: cuối cùng, khi bạn biết nó hoạt động chính xác để hoàn thành, chỉ cần cho người dùng biết nội dung kết quả nằm ở đâu và cung cấp một lệnh được đề xuất cho họ để tự di chuyển nó.
giám mục

Về lý thuyết, vâng, có những cách vô hạn điều này có thể thất bại. Trong thực tế, phần lớn thời gian thất bại này là do đường dẫn được cung cấp không hợp lệ. Tôi không thể ngăn chặn một cách hợp lý một số người dùng lừa đảo đồng thời sửa đổi FS để phá vỡ công việc ở giữa hoạt động, nhưng tôi chắc chắn có thể kiểm tra nguyên nhân thất bại số 1 của một đường dẫn không hợp lệ hoặc không thể ghi.
BeeOnRope

2

TL; DR:

: >> "${userfile}"

Không giống như <> "${userfile}"hoặc touch "${userfile}", điều này sẽ không thực hiện bất kỳ sửa đổi giả nào đối với dấu thời gian của tệp và cũng sẽ hoạt động với các tệp chỉ ghi.


Từ OP:

Tôi muốn kiểm tra hợp lý nếu tập tin có thể được tạo / ghi đè, nhưng không thực sự tạo ra nó.

Và từ nhận xét của bạn đến câu trả lời của tôi từ góc độ UX :

Phần lớn thời gian thất bại này là do đường dẫn được cung cấp không hợp lệ. Tôi không thể ngăn chặn một cách hợp lý một số người dùng lừa đảo đồng thời sửa đổi FS để phá vỡ công việc ở giữa hoạt động, nhưng tôi chắc chắn có thể kiểm tra nguyên nhân thất bại số 1 của một đường dẫn không hợp lệ hoặc không thể ghi.

Thử nghiệm đáng tin cậy duy nhất là vào open(2)tệp, bởi vì chỉ có giải quyết mọi câu hỏi về khả năng ghi: đường dẫn, quyền sở hữu, hệ thống tệp, mạng, bối cảnh bảo mật, v.v. Bất kỳ thử nghiệm nào khác sẽ giải quyết một phần khả năng ghi, nhưng không phải là một phần khác. Nếu bạn muốn có một tập hợp các bài kiểm tra, cuối cùng bạn sẽ phải chọn những gì quan trọng với bạn.

Nhưng đây là một suy nghĩ khác. Từ những gì tôi hiểu:

  1. quá trình tạo nội dung được thực hiện lâu dài và
  2. tập tin đích nên được để ở trạng thái nhất quán.

Bạn muốn thực hiện kiểm tra trước này vì # 1 và bạn không muốn sử dụng tệp hiện có vì # 2. Vậy tại sao bạn không yêu cầu shell mở tệp để chắp thêm, nhưng thực tế không nối thêm gì?

$ tree -ps
.
├── [dr-x------        4096]  dir_r
├── [drwx------        4096]  dir_w
├── [-r--------           0]  file_r
└── [-rw-------           0]  file_w

$ for p in file_r dir_r/foo file_w dir_w/foo; do : >> $p; done
-bash: file_r: Permission denied
-bash: dir_r/foo: Permission denied

$ tree -ps
.
├── [dr-x------        4096]  dir_r
├── [drwx------        4096]  dir_w
   └── [-rw-rw-r--           0]  foo
├── [-r--------           0]  file_r
└── [-rw-------           0]  file_w

Dưới mui xe, điều này giải quyết câu hỏi có thể ghi chính xác như mong muốn:

open("dir_w/foo", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3

nhưng không sửa đổi nội dung hoặc siêu dữ liệu của tệp. Bây giờ, vâng, cách tiếp cận này:

  • không cho bạn biết nếu tệp chỉ được nối thêm, đây có thể là một vấn đề khi bạn tiếp tục cập nhật nó khi kết thúc quá trình tạo nội dung của mình. Bạn có thể phát hiện điều này, ở một mức độ, với lsattrvà phản ứng tương ứng.
  • tạo một tệp mà trước đây không tồn tại, nếu đó là trường hợp: giảm thiểu điều này bằng một cách chọn lọc rm.

Mặc dù tôi cho rằng (trong câu trả lời khác của tôi) rằng cách tiếp cận thân thiện với người dùng nhất là tạo một tệp tạm thời mà người dùng phải di chuyển, tôi nghĩ rằng đây là cách tiếp cận ít người dùng nhất để kiểm tra hoàn toàn đầu vào của họ.

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.