Tuy nhiên, tôi muốn thảo luận về một số cách tiếp cận bị hỏng và bán khả thi khác nhau bằng cách sử dụng ps
và nhiều cảnh báo họ có, vì tôi cứ thấy mọi người sử dụng chúng.
Câu trả lời này thực sự là câu trả lời cho "Tại sao không sử dụng ps
và grep
xử lý khóa trong vỏ?"
Cách tiếp cận bị hỏng # 1
Đầu tiên, một cách tiếp cận được đưa ra trong một câu trả lời khác có một vài upvote mặc dù thực tế là nó không (và không bao giờ có thể) hoạt động và rõ ràng không bao giờ được thử nghiệm:
running_proc=$(ps -C bash -o pid=,cmd= | grep my_script);
if [[ "$running_proc" != "$$ bash my_script" ]]; do
echo Already locked
exit 6
fi
Chúng ta hãy sửa các lỗi cú pháp và các ps
đối số bị hỏng và nhận:
running_proc=$(ps -C bash -o pid,cmd | grep "$0");
echo "$running_proc"
if [[ "$running_proc" != "$$ bash $0" ]]; then
echo Already locked
exit 6
fi
Kịch bản này sẽ luôn thoát 6, mọi lúc, bất kể bạn chạy nó như thế nào.
Nếu bạn chạy nó với ./myscript
, thì ps
đầu ra sẽ chỉ 12345 -bash
, không khớp với chuỗi yêu cầu 12345 bash ./myscript
, do đó sẽ thất bại.
Nếu bạn chạy nó với bash myscript
, mọi thứ trở nên thú vị hơn. Quá trình bash dĩa để chạy các đường ống, và các con shell chạy ps
và grep
. Cả vỏ ban đầu và vỏ con sẽ hiển thị ở ps
đầu ra, đại loại như thế này:
25793 bash myscript
25795 bash myscript
Đó không phải là đầu ra dự kiến $$ bash $0
, vì vậy tập lệnh của bạn sẽ thoát.
Cách tiếp cận bị hỏng # 2
Bây giờ trong tất cả sự công bằng cho người dùng đã viết cách tiếp cận bị hỏng # 1, tôi đã làm một cái gì đó tương tự khi lần đầu tiên tôi thử điều này:
if otherpids="$(pgrep -f "$0" | grep -vFx "$$")" ; then
echo >&2 "There are other copies of the script running; exiting."
ps >&2 -fq "${otherpids//$'\n'/ }" # -q takes about a tenth the time as -p
exit 1
fi
Điều này gần như hoạt động. Nhưng thực tế của việc chạy để chạy đường ống ném điều này ra. Vì vậy, cái này sẽ luôn luôn thoát.
Cách tiếp cận không đáng tin cậy # 3
pids_this_script="$(pgrep -f "$0")"
if not_this_process="$(echo "$pids_this_script" | grep -vFx "$$")"; then
echo >&2 "There are other copies of this script running; exiting."
ps -fq "${not_this_process//$'\n'/ }"
exit 1
fi
Phiên bản này tránh vấn đề giả mạo đường ống trong cách tiếp cận # 2 bằng cách trước tiên nhận tất cả các PID có tập lệnh hiện tại trong các đối số dòng lệnh của chúng, sau đó lọc riêng pidlist đó để bỏ qua PID của tập lệnh hiện tại.
Điều này có thể hoạt động ... cung cấp không có quy trình nào khác có dòng lệnh khớp với $0
và cung cấp tập lệnh luôn được gọi theo cùng một cách (ví dụ: nếu nó được gọi với một đường dẫn tương đối và sau đó là một đường dẫn tuyệt đối, thì trường hợp sau sẽ không chú ý đến trước ).
Cách tiếp cận không đáng tin cậy # 4
Vậy điều gì sẽ xảy ra nếu chúng ta bỏ qua việc kiểm tra dòng lệnh đầy đủ, vì điều đó có thể không cho thấy tập lệnh thực sự đang chạy và lsof
thay vào đó hãy kiểm tra để tìm tất cả các quy trình mở tập lệnh này?
Vâng, vâng, cách tiếp cận này thực sự không quá tệ:
if otherpids="$(lsof -t "$0" | grep -vFx "$$")"; then
echo >&2 "Error: There are other processes that have this script open - most likely other copies of the script running. Exiting to avoid conflicts."
ps >&2 -fq "${otherpids//$'\n'/ }"
exit 1
fi
Tất nhiên, nếu một bản sao của tập lệnh đang chạy, thì phiên bản mới sẽ khởi động tốt và bạn sẽ có hai bản sao đang chạy.
Hoặc nếu tập lệnh đang chạy được sửa đổi (ví dụ với Vim hoặc với a git checkout
), thì phiên bản "mới" của tập lệnh sẽ khởi động không có vấn đề gì, vì cả Vim và git checkout
dẫn đến một tệp mới (một nút mới) thay cho người già.
Tuy nhiên, nếu kịch bản không bao giờ được sửa đổi và không bao giờ được sao chép, thì phiên bản này là khá tốt. Không có điều kiện chạy đua vì tệp tập lệnh đã được mở trước khi có thể đạt được kiểm tra.
Vẫn có thể có dương tính giả nếu một quá trình khác mở tệp tập lệnh, nhưng lưu ý rằng ngay cả khi nó mở để chỉnh sửa trong Vim, vim không thực sự giữ tệp tập lệnh mở nên sẽ không dẫn đến kết quả dương tính giả.
Nhưng hãy nhớ, không sử dụng phương pháp này nếu tập lệnh có thể được chỉnh sửa hoặc sao chép vì bạn sẽ nhận được âm bản giả, tức là nhiều trường hợp đang chạy cùng một lúc - vì vậy thực tế việc chỉnh sửa bằng Vim không mang lại kết quả dương tính giả không quan trọng để bạn Mặc dù vậy, tôi đề cập đến nó, bởi vì cách tiếp cận # 3 không cho kết quả dương tính giả (nghĩa là từ chối bắt đầu) nếu bạn mở tập lệnh với Vim.
Vậy phải làm sao?
Các câu trả lời hàng đầu bình chọn cho câu hỏi này đưa ra một cách tiếp cận tốt rắn.
Có lẽ bạn có thể viết một cái tốt hơn ... nhưng nếu bạn không hiểu tất cả các vấn đề và hãy cẩn thận với tất cả các cách tiếp cận ở trên, bạn không có khả năng viết một phương pháp khóa để tránh tất cả.
kill
ed; và nó có vẻ là một thực hành tốt để lưu trữ pid của chính mình trong kho khóa, thay vì chỉ chạm vào nó.