Thoát một biến để sử dụng làm nội dung của tập lệnh khác


18

Câu hỏi này không phải là về cách viết một chuỗi ký tự thoát đúng. Tôi không thể tìm thấy bất kỳ câu hỏi liên quan nào không phải là về cách thoát các biến để tiêu thụ trực tiếp trong một tập lệnh hoặc bởi các chương trình khác.

Mục tiêu của tôi là cho phép một tập lệnh tạo các tập lệnh khác. Điều này là do các tác vụ trong các tập lệnh được tạo sẽ chạy bất kỳ nơi nào từ 0 đến n lần trên một máy khác và dữ liệu mà chúng được tạo có thể thay đổi trước khi chúng chạy (một lần nữa), do đó, thực hiện các thao tác trực tiếp, qua mạng sẽ không làm việc.

Đưa ra một biến đã biết có thể chứa các ký tự đặc biệt như dấu ngoặc đơn, tôi cần viết nó ra dưới dạng một chuỗi ký tự thoát hoàn toàn, ví dụ: một biến foochứa bar'bazsẽ xuất hiện trong tập lệnh được tạo như:

qux='bar'\''baz'

mà sẽ được viết bằng cách nối thêm "qux=$foo_esc"các dòng script khác. Tôi đã làm nó bằng cách sử dụng Perl như thế này:

foo_esc="'`perl -pe 's/('\'')/\\1\\\\\\1\\1/g' <<<"$foo"`'"

nhưng điều này có vẻ như quá mức cần thiết.

Tôi đã không thành công khi làm điều đó với bash một mình. Tôi đã thử nhiều biến thể trong số này:

foo_esc="'${file//\'/\'\\\'\'}'"
foo_esc="'${file//\'/'\\''}'"

nhưng dấu gạch chéo xuất hiện trong đầu ra (khi tôi thực hiện echo "$foo") hoặc chúng gây ra lỗi cú pháp (mong đợi thêm đầu vào nếu được thực hiện từ trình bao).


aliasvà / hoặc setkhá phổ biến
mikeerv

@mikeerv Xin lỗi, tôi không chắc ý của bạn là gì.
Walf

alias "varname=$varname" varnamehoặcvar=value set
mikeerv

2
@mikeerv Đó không phải là bối cảnh đủ để tôi hiểu đề xuất của bạn là gì, cũng như cách tôi sẽ sử dụng nó như một phương pháp chung để thoát khỏi bất kỳ biến nào. Đó là một vấn đề được giải quyết, bạn đời.
Walf

Câu trả lời:


25

Bash có một tùy chọn mở rộng tham số cho chính xác trường hợp này :

${parameter@Q}Mở rộng là một chuỗi là giá trị của tham số được trích dẫn theo định dạng có thể được sử dụng lại làm đầu vào.

Vì vậy, trong trường hợp này:

foo_esc="${foo@Q}"

Điều này được hỗ trợ trong Bash 4.4 trở lên. Cũng có một số tùy chọn cho các hình thức mở rộng khác và để tạo ra các câu lệnh gán hoàn chỉnh ( @A).


7
Gọn gàng, nhưng chỉ có 4.2 mà cho bad substitution.
Walf

3
Tương đương vỏ Z là "${foo:q}".
JdeBP

Thực sự cứu mạng tôi! "${foo@Q}"làm!
hao

@JdeBP tương đương với vỏ Z không hoạt động. Bất kỳ ý tưởng khác cho zsh?
Steven Shaw

1
Tôi đã tìm thấy câu trả lời: "$ {(@ qq) foo}"
Steven Shaw

10

Bash cung cấp một printfnội dung với trình %qxác định định dạng, thực hiện thoát vỏ cho bạn, ngay cả trong các phiên bản cũ hơn (<4.0) của Bash:

printf '[%q]\n' "Ne'er do well"
# Prints [Ne\'er\ do\ well]

printf '[%q]\n' 'Sneaky injection $( whoami ) `ls /root`'
# Prints [Sneaky\ injection\ \$\(\ whoami\ \)\ \`ls\ /root\`]

Thủ thuật này cũng có thể được sử dụng để trả về các mảng dữ liệu từ một hàm:

function getData()
{
  printf '%q ' "He'll say hi" 'or `whoami`' 'and then $( byebye )'
}

declare -a DATA="( $( getData ) )"
printf 'DATA: [%q]\n' "${DATA[@]}"
# Prints:
# DATA: [He\'ll\ say\ hi]
# DATA: [or\ \`whoami\`]
# DATA: [and\ then\ \$\(\ byebye\ \)]

Lưu ý rằng phần mềm printfdựng sẵn Bash khác với printftiện ích đi kèm với hầu hết các hệ điều hành giống Unix. Nếu, vì một số lý do, printflệnh gọi tiện ích thay vì dựng sẵn, bạn luôn có thể thực thi builtin printfthay thế.


Tôi không chắc điều đó sẽ hữu ích như thế nào nếu những gì tôi cần được in sẽ 'Ne'\''er do well', v.v., tức là các trích dẫn có trong đầu ra.
Walf

1
@Walf Tôi nghĩ rằng bạn không hiểu rằng hai hình thức là tương đương nhau và cả hai đều hoàn toàn an toàn như nhau. Eg [[ 'Ne'\''er do well' == Ne\'er\ do\ well ]] && echo 'equivalent!'sẽ lặp lạiequivalent!
Dejay Clayton 30/07/18

Tôi đã bỏ lỡ điều đó: P tuy nhiên tôi thích biểu mẫu được trích dẫn vì nó dễ đọc hơn trong trình xem / trình chỉnh sửa tô sáng cú pháp.
Walf

@Walf có vẻ như cách tiếp cận của bạn khá nguy hiểm, vì trong ví dụ Perl của bạn, việc chuyển một giá trị như 'hello'kết quả trong giá trị không chính xác ''\''hello'', có một chuỗi rỗng hàng đầu không cần thiết (hai dấu ngoặc đơn đầu tiên) và một trích dẫn không phù hợp.
Dejay Clayton

1
@Walf, để làm rõ, $'escape-these-chars'là tính năng trích dẫn ANSI-C của Bash khiến tất cả các ký tự trong chuỗi được chỉ định bị thoát. Do đó, để dễ dàng tạo một chuỗi ký tự có chứa một dòng mới trong tên tệp (ví dụ: $'first-line\nsecond-line')sử dụng \ntrong cấu trúc này.
Dejay Clayton

7

Tôi đoán tôi đã không RTFM. Nó có thể được thực hiện như vậy:

q_mid=\'\\\'\'
foo_esc="'${foo//\'/$q_mid}'"

Sau đó echo "$foo_esc"đưa ra dự kiến'bar'\''baz'


Làm thế nào tôi thực sự sử dụng nó với một chức năng:

function esc_var {
    local mid_q=\'\\\'\'
    printf '%s' "'${1//\'/$mid_q}'"
}

...

foo_esc="`esc_var "$foo"`"

Sửa đổi điều này để sử dụng tích printfhợp từ giải pháp của Dejay:

function esc_vars {
    printf '%q' "$@"
}

3
Tôi sẽ sử dụng giải pháp @ michael-homer nếu phiên bản của tôi hỗ trợ nó.
Walf

3

Có một số giải pháp để trích dẫn một giá trị var:

  1. bí danh
    Trong hầu hết các shell (nơi có bí danh) (ngoại trừ csh, tcsh và có lẽ những người khác csh thích):

    $ alias qux=bar\'baz
    $ alias qux
    qux='bar'\''baz'

    Vâng, điều này hoạt động trong nhiều shvỏ giống như gạch ngang hoặc tro.

  2. thiết lập
    Cũng trong hầu hết các shell (một lần nữa, không phải csh):

    $ qux=bar\'baz
    $ set | grep '^qux='
    qux='bar'\''baz'
  3. sắp chữ
    Trong một số shell (ít nhất là ksh, bash và zsh):

    $ qux=bar\'baz
    $ typeset -p qux
    typeset qux='bar'\''baz'             # this is zsh, quoting style may
                                         # be different for other shells.
  4. Xuất khẩu
    đầu tiên làm:

    export qux=bar\'baz

    Sau đó sử dụng:
    export -p | grep 'qux=' export -p | grep 'qux='
    export -p qux

  5. trích dẫn
    echo "${qux@Q}"
    echo "${(qq)qux}" # từ một đến bốn q có thể được sử dụng.


Cách tiếp cận bí danh là thông minh và có vẻ như được chỉ định bởi POSIX. Đối với tính di động tối đa tôi nghĩ rằng đây là cách để đi. Tôi tin rằng những đề nghị liên quan đến grepvới exporthoặc setcó thể phá vỡ trên các biến chứa newlines nhúng.
jw013
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.