Làm cách nào để khôi phục giá trị của các tùy chọn shell như `set -x`?


47

Tôi muốn set -xở phần đầu của kịch bản của mình và "hoàn tác" nó (quay trở lại trạng thái trước khi tôi đặt nó) sau đó thay vì cài đặt một cách mù quáng +x. Điều này có thể không?

PS: Tôi đã kiểm tra ở đây ; điều đó dường như không trả lời câu hỏi của tôi như tôi có thể nói.

Câu trả lời:


59

trừu tượng

Để đảo ngược set -xchỉ cần thực hiện a set +x. Hầu hết thời gian, đảo ngược của một chuỗi set -strlà cùng một chuỗi với +: set +str.

Nói chung, để khôi phục tất cả (đọc bên dưới về bash errexit) các tùy chọn shell (đã thay đổi bằng setlệnh) bạn có thể làm (cũng đọc bên dưới về shoptcác tùy chọn bash ):

oldstate="$(set +o); set -$-"                # POSIXly store all set options.
.
.
set -vx; eval "$oldstate"         # restore all options stored.

Mô tả dài hơn

bash

Lệnh này:

shopt -po xtrace

sẽ tạo ra một chuỗi thực thi phản ánh trạng thái của tùy chọn. Các pphương tiện cờ in, và các oquy định cụ thể cờ mà chúng ta đang hỏi về tùy chọn (s) thiết lập bởi các setlệnh (như trái ngược với tùy chọn (s) thiết lập bởi các shoptlệnh). Bạn có thể gán chuỗi này cho một biến và thực hiện biến ở cuối tập lệnh của bạn để khôi phục trạng thái ban đầu.

# store state of xtrace option.
tracestate="$(shopt -po xtrace)"

# change xtrace as needed
echo "some commands with xtrace as externally selected"
set -x
echo "some commands with xtrace set"

# restore the value of xtrace to its original value.
eval "$tracestate"

Giải pháp này hoạt động cho nhiều tùy chọn cùng một lúc:

oldstate="$(shopt -po xtrace noglob errexit)"

# change options as needed
set -x
set +x
set -f
set -e
set -x

# restore to recorded state:
set +vx; eval "$oldstate"

Thêm set +vxtránh việc in một danh sách dài các tùy chọn.


Và, nếu bạn không liệt kê bất kỳ tên tùy chọn nào,

oldstate="$(shopt -po)"

nó cung cấp cho bạn các giá trị của tất cả các tùy chọn. Và, nếu bạn bỏ ocờ, bạn có thể thực hiện những điều tương tự với shoptcác tùy chọn:

# store state of dotglob option.
dglobstate="$(shopt -p dotglob)"

# store state of all options.
oldstate="$(shopt -p)"

Nếu bạn cần kiểm tra xem một settùy chọn đã được đặt hay chưa, cách thành ngữ nhất (Bash) để thực hiện là:

[[ -o xtrace ]]

tốt hơn so với hai bài kiểm tra tương tự khác:

  1. [[ $- =~ x ]]
  2. [[ $- == *x* ]]

Với bất kỳ thử nghiệm nào, điều này hoạt động:

# record the state of the xtrace option in ts (tracestate):
[ -o xtrace ] && ts='set -x' || ts='set +x'

# change xtrace as needed
echo "some commands with xtrace as externally selected"
set -x
echo "some commands with xtrace set"

# set the xtrace option back to what it was.
eval "$ts"

Đây là cách kiểm tra trạng thái của một shopttùy chọn:

if shopt -q dotglob
then
        # dotglob is set, so “echo .* *” would list the dot files twice.
        echo *
else
        # dotglob is not set.  Warning: the below will list “.” and “..”.
        echo .* *
fi

MỘT BỘ MÔ TẢ CHÍNH THỨC CUNG CẤP MỘT TIÊU CHUẨN CHO THIẾT KẾ HỆ ĐIỀU HÀNH, ĐẶC BIỆT LÀ CÁC MÔ TẢ TƯƠNG THÍCH VỚI UNIX

Một giải pháp đơn giản, tuân thủ POSIX để lưu trữ tất cả các settùy chọn là:

set +o

được mô tả trong tiêu chuẩn POSIX như:

+ o

    Viết các cài đặt tùy chọn hiện tại vào đầu ra tiêu chuẩn ở định dạng phù hợp để tái tạo lại trình bao dưới dạng các lệnh đạt được các cài đặt tùy chọn tương tự.

Rất dễ:

oldstate=$(set +o)

sẽ bảo toàn các giá trị cho tất cả các tùy chọn được đặt bằng setlệnh.

Một lần nữa, khôi phục các tùy chọn về giá trị ban đầu của chúng là vấn đề thực thi biến:

set +vx; eval "$oldstate"

Điều này chính xác tương đương với việc sử dụng Bash's shopt -po. Lưu ý rằng nó sẽ không bao gồm tất cả các tùy chọn Bash có thể , vì một số tùy chọn được đặt bởi shopt.

bash trường hợp đặc biệt

Có nhiều tùy chọn shell khác được liệt kê shopttrong bash:

$ shopt
autocd          off
cdable_vars     off
cdspell         off
checkhash       off
checkjobs       off
checkwinsize    on
cmdhist         on
compat31        off
compat32        off
compat40        off
compat41        off
compat42        off
compat43        off
complete_fullquote  on
direxpand       off
dirspell        off
dotglob         off
execfail        off
expand_aliases  on
extdebug        off
extglob         off
extquote        on
failglob        off
force_fignore   on
globasciiranges off
globstar        on
gnu_errfmt      off
histappend      on
histreedit      off
histverify      on
hostcomplete    on
huponexit       off
inherit_errexit off
interactive_comments    on
lastpipe        on
lithist         off
login_shell     off
mailwarn        off
no_empty_cmd_completion off
nocaseglob      off
nocasematch     off
nullglob        off
progcomp        on
promptvars      on
restricted_shell    off
shift_verbose   off
sourcepath      on
xpg_echo        off

Chúng có thể được thêm vào biến được đặt ở trên và được khôi phục theo cùng một cách:

$ oldstate="$oldstate;$(shopt -p)"
.
.                                   # change options as needed.
.
$ eval "$oldstate" 

Có thể làm (việc $-được nối để đảm bảo errexitđược bảo tồn):

oldstate="$(shopt -po; shopt -p); set -$-"

set +vx; eval "$oldstate"             # use to restore all options.

Lưu ý : mỗi shell có một cách hơi khác nhau để xây dựng danh sách các tùy chọn được đặt hoặc không đặt (không đề cập đến các tùy chọn khác nhau được xác định), do đó, các chuỗi không di động giữa các shell, nhưng hợp lệ cho cùng một shell.

trường hợp đặc biệt zsh

zshcũng hoạt động chính xác (theo POSIX) kể từ phiên bản 5.3. Trong các phiên bản trước, nó chỉ tuân theo POSIX một phần với set +onó in các tùy chọn ở định dạng phù hợp để tái tạo vỏ như các lệnh, nhưng chỉ cho các tùy chọn đã đặt (nó không in các tùy chọn chưa đặt ).

trường hợp đặc biệt mksh

Mksh (và do hậu quả lksh) chưa có (MIRBSD KSH R54 2016/11/11) có thể làm điều này. Hướng dẫn mksh có chứa điều này:

Trong phiên bản tương lai, set + o sẽ hành xử các lệnh in và tuân thủ POSIX để khôi phục các tùy chọn hiện tại thay thế.

trường hợp đặc biệt

Trong bash, giá trị của set -e( errexit) được đặt lại bên trong các vỏ con, điều này gây khó khăn cho việc nắm bắt giá trị của nó với set +obên trong một vỏ con $ ( Thay ).

Như một cách giải quyết, sử dụng:

oldstate="$(set +o); set -$-"

21

Với trình bao và các dẫn xuất Almquist ( ít nhất là dashNetBSD / FreeBSD sh) và bash4.4 trở lên, bạn có thể thực hiện các tùy chọn cục bộ cho một hàm với local -( $-biến biến cục bộ nếu bạn muốn):

$ bash-4.4 -c 'f() { local -; set -x; echo test; }; f; echo no trace'
+ echo test
test
no trace

Điều đó không áp dụng cho các tệp có nguồn gốc , nhưng bạn có thể xác định lại sourcesource() { . "$@"; }để giải quyết vấn đề đó.

Với ksh88, thay đổi tùy chọn là cục bộ của chức năng theo mặc định. Với ksh93, đó chỉ là trường hợp cho các hàm được xác định theo function f { ...; }cú pháp (và phạm vi là tĩnh so với phạm vi động được sử dụng trong các hệ vỏ khác bao gồm ksh88):

$ ksh93 -c 'function f { set -x; echo test; }; f; echo no trace'
+ echo test
test
no trace

Trong zsh, điều đó được thực hiện với localoptionstùy chọn:

$ zsh -c 'f() { set -o localoptions; set -x; echo test; }; f; echo no trace'
+f:0> echo test
test
no trace

POSIXly, bạn có thể làm:

case $- in
  (*x*) restore=;;
  (*) restore='set +x'; set -x
esac
echo test
{ eval "$restore";} 2> /dev/null
echo no trace

Tuy nhiên, một số shell sẽ xuất ra + 2> /dev/nullkhi khôi phục (và bạn sẽ thấy dấu vết của casecấu trúc đó tất nhiên nếu set -xđã được bật). Cách tiếp cận đó cũng không được đăng ký lại (như nếu bạn thực hiện điều đó trong một hàm gọi chính nó hoặc một hàm khác sử dụng cùng một thủ thuật).

Xem https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh (phạm vi cục bộ cho các biến và tùy chọn cho shell POSIX) để biết cách triển khai ngăn xếp hoạt động xung quanh đó.

Với bất kỳ shell nào, bạn có thể sử dụng các lớp con để giới hạn phạm vi của các tùy chọn

$ sh -c 'f() (set -x; echo test); f; echo no trace'
+ echo test
test
no trace

Tuy nhiên, điều đó giới hạn phạm vi của mọi thứ (biến, hàm, bí danh, chuyển hướng, thư mục làm việc hiện tại ...), không chỉ các tùy chọn.


bash 4.4 đã xuất hiện 4 ngày trước (16 tháng 9 năm 2016), vì vậy có lẽ bạn sẽ phải tự biên dịch lại, nhưng tại sao không
edi9999

Dường như với tôi đó oldstate=$(set +o)là một cách đơn giản hơn (và POSIX) để lưu trữ tất cả các tùy chọn.
sorontar

@sorontar, điểm tốt mặc dù điều đó không hoạt động đối với việc triển khai shell như pdksh/ mksh(và các dẫn xuất pdksh khác) hoặc zshkhi set +ochỉ xuất ra độ lệch từ cài đặt mặc định. Điều đó sẽ làm việc cho bash / dash / yash nhưng không thể di động.
Stéphane Chazelas

13

Bạn có thể đọc $-biến ở đầu để xem có -xđược đặt hay không và sau đó lưu nó vào một biến, ví dụ

if [[ $- == *x* ]]; then
  was_x_set=1
else
  was_x_set=0
fi

Từ hướng dẫn Bash :

($ -, một dấu gạch nối.) Mở rộng sang các cờ tùy chọn hiện tại như được chỉ định khi gọi, bằng lệnh dựng sẵn hoặc các lệnh được đặt bởi chính trình bao (chẳng hạn như tùy chọn -i).


7
[[ $SHELLOPTS =~ xtrace ]] && wasset=1
set -x
echo rest of your script
[[ $wasset -eq 0 ]] && set +x

Trong bash, $ SHELLOPTS được đặt với các cờ được bật. Kiểm tra nó trước khi bạn bật xtrace và chỉ đặt lại xtrace nếu nó bị tắt trước đó.


5

Chỉ cần nêu rõ ràng, nếu set -xnó có hiệu lực trong suốt thời gian của tập lệnh và đây chỉ là một biện pháp thử nghiệm tạm thời (không phải là một phần vĩnh viễn của đầu ra), thì hãy gọi tập lệnh w / -xtùy chọn, ví dụ:

$ bash -x path_to_script.sh

... hoặc, tạm thời thay đổi tập lệnh (dòng đầu tiên) để bật đầu ra gỡ lỗi bằng cách thêm -xtùy chọn:

#!/bin/bash -x
...rest of script...

Tôi nhận ra rằng điều này có thể quá rộng đối với những gì bạn muốn, nhưng đó là cách đơn giản và nhanh nhất để bật / tắt, mà không làm phức tạp quá nhiều tập lệnh với những thứ tạm thời mà bạn có thể muốn xóa (theo kinh nghiệm của tôi).


3

Điều này cung cấp các chức năng để lưu và khôi phục các cờ hiển thị thông qua $-tham số đặc biệt POSIX . Chúng tôi sử dụng localphần mở rộng cho các biến cục bộ. Trong tập lệnh di động tuân thủ POSIX, các biến toàn cục sẽ được sử dụng (không có localtừ khóa):

save_opts()
{
  echo $-
}

restore_opts()
{
  local saved=$1
  local on
  local off=$-

  while [ ${#saved} -gt 0 ] ; do
    local rest=${saved#?}
    local first=${saved%$rest}

    if echo $off | grep -q $first ; then
      off=$(echo $off | tr -d $first)
    fi

    on="$on$first"
    saved=$rest
  done

  set ${on+"-$on"} ${off+"+$off"}
}

Điều này được sử dụng tương tự như cách các cờ ngắt được lưu và khôi phục trong nhân Linux:

Shell:                                Kernel:

flags=$(save_opts)                    long flags;
                                      save_flags (flags);

set -x  # or any other                local_irq_disable(); /* disable irqs on this core */

# ... -x enabled ...                  /* ... interrupts disabled ... */

restore_opts $flags                   restore_flags(flags);

# ... x restored ...                  /* ... interrupts restored ... */

Điều này sẽ không hoạt động đối với bất kỳ tùy chọn mở rộng nào không có trong $-biến.

Chỉ cần lưu ý rằng POSIX có những gì tôi đang tìm kiếm: +ođối số setkhông phải là một tùy chọn, mà là một lệnh loại bỏ một loạt các lệnh, nếu eval-ed sẽ khôi phục các tùy chọn. Vì thế:

flags=$(set +o)

set -x

# ...

eval "$flags"

Đó là nó.

Một vấn đề nhỏ là nếu -xtùy chọn được bật trước đó, sẽ thấy evalmột loạt các set -olệnh xấu xí . Để loại bỏ điều này, chúng ta có thể làm như sau:

set +x             # turn off trace not to see the flurry of set -o.
eval "$flags"      # restore flags

1

Bạn có thể sử dụng một vỏ phụ.

(
   set 
   do stuff
)
Other stuff, that set does not apply to
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.