Làm thế nào để lặp lại các lệnh trong tập lệnh shell bash, nhưng không thực thi chúng?


11

Có cách nào để chạy một kịch bản shell với việc lặp lại các lệnh nhưng thực sự không thực thi chúng không?

Giả sử tôi có tập lệnh xóa tập tin có tên được lưu trữ trong một biến:

#!/bin/bash
set -v
FN="filename"
rm -f ${FN}

Thêm set -vsẽ lặp lại các lệnh sau trước khi thực hiện:

$ ./scr.sh
FN="filename"
rm -f ${FN}

Bây giờ, tôi muốn xem luồng của tập lệnh này với việc thực sự xóa tệp. IOW, tôi muốn ngăn chặn mọi ảnh hưởng đến môi trường bên ngoài và hệ thống tập tin. Điều này có thể không?

Tôi có thể gói tất cả các lệnh bằng một echolệnh, nhưng thật mệt mỏi cho một kịch bản dài.


Tôi không phải là một chuyên gia về bash-scripting, nhưng nếu tôi viết nó bằng ngôn ngữ lập trình khác, tôi sẽ lưu trữ tất cả các lệnh trong một mảng. bằng cách này, nó sẽ dễ dàng lặp lại và thực hiện các lệnh hoặc in ra.
hugo der hungrige

Câu trả lời:


8

Không có cách nào để bước qua một kịch bản để xem nó sẽ thực thi như thế nào mà không thực sự làm điều đó. Trong ví dụ của bạn, không có ifcâu lệnh hoặc vòng lặp. Nhưng trong các kịch bản thực, thường có rất nhiều câu điều kiện. Nhánh nào sẽ được lấy thường sẽ phụ thuộc vào những gì đã xảy ra khi shell chạy lệnh trước đó. Nếu nó không chạy lệnh, shell sẽ không biết được nó sẽ tạo ra đầu ra nào hoặc mã trả về sẽ là gì, mà nhánh điều kiện hoặc câu lệnh gán tiếp theo có thể phụ thuộc vào.

Nếu vấn đề là bạn muốn xem xét kỹ một kịch bản và biết nó làm gì trước khi bạn chạy nó, thì đó không phải là một ý tưởng tồi. Nhưng thực tế, khoảng cách tốt nhất để làm điều đó là bởi chỉ duyệt các tập tin với lesshoặc vihoặc một cái gì đó tương tự.

Thêm

Nếu bạn đang phát triển tập lệnh và bạn muốn thực hiện lần đầu tiên, kiểm tra logic nhưng không thực sự gây ra thiệt hại nếu bạn gặp lỗi, giải pháp tôi thường có thể sử dụng là chỉ sửa đổi các tuyên bố có thể gây ra bất kỳ thiệt hại bằng cách dán echolên phía trước.

Điều này thường hoạt động trong các tập lệnh thực tế điển hình vì (a) tạo danh sách các mục bạn sẽ lặp lại hoặc giá trị mà bạn sẽ đặt biến thường có thể được tạo mà không thay đổi hệ thống tệp và (b) thường đủ để giả sử rằng nếu bạn đã chạy lệnh, nó sẽ thành công.


Cảm ơn. Nó sẽ làm tôi ngạc nhiên nếu có một cách, chính xác là do lý do bạn đề cập, nhưng tôi đã quyết định thử nó bằng mọi cách. Lý do cho điều này là vì tôi đang phát triển một tập lệnh di chuyển các tệp trên nhiều máy trong mạng và thực hiện một số hành động trên các máy chủ từ xa. Tôi muốn xem một luồng kịch bản trước khi thực sự cam kết thay đổi hệ thống tập tin. Sẽ thật tẻ nhạt khi đi qua tất cả các máy từ xa để sửa lỗi tập tin ...
ysap

1
Ngoài ra: Để chuyển qua tập lệnh, bạn có thể bắt đầu tập lệnh trap read debugsẽ đọc sau mỗi dòng thực thi và sau đó bạn có thể bước qua tập lệnh một cách chậm rãi (điều này rất hữu ích nếu bạn có một tập lệnh thực sự dài và muốn kiểm tra nó lần đầu tiên)
netigger

8

Tôi nghĩ rằng bạn sẽ phải lặp lại - tuy nhiên nếu bạn đặt lệnh vào một biến bạn có thể bật và tắt nó. Ngoài ra, lệnh có thể là một chức năng và tinh vi như bạn muốn.

#!/bin/bash
echo 'Debug'
  D=echo
  :>| testy
  ls -l testy
  $D rm -f testy
  ls -l testy
echo $'\n\nLive'
  D=
  :>| testy
  ls -l testy
  $D rm -f testy
  ls -l testy

!$ > ./conditional.sh
Debug
-rw-rw-r-- 1 nobody nobody 0 2013-01-18 15:28 testy
rm -f testy
-rw-rw-r-- 1 nobody nobody 0 2013-01-18 15:28 testy

Live -rw-rw-r-- 1 nobody nobody 0 2013-01-18 15:28 testy ls: cannot access testy: No such file or directory


Tôi yêu cầu trích dẫn trong các lệnh echo / dont-echo và sau đó phải sử dụng D=evaltrong phần trực tiếp. Nhưng câu trả lời tốt đẹp!
Jasper

5

Bạn theo dõi luồng bằng tùy chọn -x cho bash, nhưng điều này vẫn sẽ thực thi các lệnh.

Điều tốt nhất bạn có thể làm là chỉ cần đặt echo hoặc nhận xét các lệnh có hiệu ứng bên ngoài như rm. Ít nhất bạn sẽ không phải thay đổi mọi dòng.


Cảm ơn. Tôi đã đề cập đến tùy chọn đó trong câu hỏi của mình, nhưng nó không thực sự giải quyết vấn đề của tôi.
ysap

Xin lỗi, tôi nghĩ bạn có nghĩa là lặp lại mỗi dòng, -x sẽ giảm điều này xuống một vài lệnh và hiển thị cho bạn các đánh giá.
parkydr

4

Tôi không biết bất kỳ phương pháp cụ thể nào cho chạy khô, nhưng chúng ta có thể sử dụng một số biện pháp phòng ngừa để hiểu một tập lệnh chưa biết.

  1. Sử dụng môi trường chroot để chạy tập lệnh gỡ lỗi của bạn. Nó sẽ hoạt động như một hộp cát và sẽ cung cấp bảo vệ chống xóa / sửa đổi các tệp hệ thống chính.
  2. Sử dụng bash -n <script>để kiểm tra cú pháp . Từ hướng dẫn sử dụng bash bash https://www.gnu.org/software/bash/manual/html_node/The-set-Builtin.html

-n Read commands but do not execute them; this may be used to check a script for syntax errors. This option is ignored by interactive shells.

  1. Đọc kịch bản ít nhất một lần. Bạn có thể sử dụng câu lệnh echo để gỡ lỗi như được đề cập trong câu trả lời của user191016 .
    • Xem ra cho mã dễ bị nghi ngờ hoặc nguy hiểm.
    • ví dụ: liên quan đến rm, các lệnh ảnh hưởng đến / dev / sda, v.v.
  2. Luôn cố gắng chạy tập lệnh như người dùng bình thường, tránh chạy tập lệnh không xác định là root.
  3. Bạn có thể xác định bí danh để tạo lệnh có thể sửa đổi các tệp thành tương tác khi bắt đầu tập lệnh của bạn Ví dụ alias rm="rm -i" alias cp="cp -i" alias mv="mv -i" Đối với sed -icác trình chỉnh sửa luồng như sed (-i sửa đổi tệp tại chỗ mà không -i nó chạy khô để tìm sed, kiểm tra trang man để biết chi tiết) Bạn có thể sao chép toàn bộ lệnh và chạy. Nó ngay trước lệnh thực tế, nó sẽ hiển thị đầu ra trên màn hình sau đó sử dụng lệnh để chờ đầu vào của người dùng tiếp tục. Ví dụ sed -i <pattern> Thay thế nó bằng sed <pattern> read -p "Press any key..." sed -i <pattern>

Ngoài ra, nó luôn được đề xuất để giữ các điểm dự phòng trong hệ thống của bạn để trong trường hợp dữ liệu khẩn cấp có thể được khôi phục.


3

Làm set –n, hoặc nối nvào set –vlệnh hiện có của bạn . Nhưng vì điều này khiến shell không đánh giá bất kỳ lệnh nào, thậm chí ifhoặc không while, bạn có thể không có được cái nhìn quá tốt về luồng hoạt động.


Cảm ơn. Đây là một khởi đầu tốt, nhưng như bạn đã đề cập, nó không cho tôi thấy luồng thực tế - trong kịch bản thực sự của tôi, tôi có một vòng lặp trong danh sách các máy chủ từ xa.
ysap

3

Đây là một cấu trúc nhỏ tôi muốn sử dụng:

#!/bin/sh

verbose=false
really=true

# parse arguments
while [ $# -ge 1 ]
do
  case "$1" in
    -v) verbose=true ;;
    -n) really=false; verbose=true ;;
  esac
  shift
done

doCmd() {
  if $verbose; then echo "$@"; fi
  if $really; then "$@"; fi
}

doCmd make foo
doCmd rm -rf /tmp/installdir
doCmd mkdir /tmp/installdir
doCmd whatever
doCmd blah blah

2

Nếu bạn muốn ngăn sửa đổi xảy ra, bạn có thể chạy tập lệnh với tư cách người dùng mà không có đặc quyền:

sudo -u nobody ./scr.sh

Trong ví dụ của bạn, điều này sẽ thực thi FN="filename"như bình thường. Mặc dù về mặt kỹ thuật nó cũng thực thi lệnh rm -f ${FN}, sẽ không có gì xảy ra nếu không ai không có quyền cần thiết để xóa tệp.

Tất nhiên, điều này sẽ gây ra vấn đề với quy trình làm việc có điều kiện. Thực tế là rmlệnh thất bại có thể ảnh hưởng đến các phần tiếp theo của tập lệnh.


Những điều chúng ta sẽ không bao giờ hiểu: Tại sao root bắt buộc phải chạy như không có ai ...
mjohnsonengr

Người dùng "không ai" không thích hợp với HĐH, nhưng bạn có thể thêm một mục trong tệp sudoers cho phép sudo không mật khẩu cho "không ai".
Dennis

Tôi không biết. Có vẻ như tập lệnh sẽ chấm dứt vì một số lỗi hoặc lỗi khác trước khi nó kết thúc.
Edward Falk
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.