ยกเว้นตัวแปรสำหรับใช้เป็นเนื้อหาของสคริปต์อื่น


20

คำถามนี้ไม่เกี่ยวกับวิธีเขียนสตริงตัวอักษรที่ใช้ Escape อย่างเหมาะสม ฉันไม่พบคำถามที่เกี่ยวข้องซึ่งไม่เกี่ยวกับวิธีการหลีกเลี่ยงตัวแปรเพื่อการบริโภคโดยตรงภายในสคริปต์หรือโดยโปรแกรมอื่น ๆ

เป้าหมายของฉันคือการเปิดใช้งานสคริปต์เพื่อสร้างสคริปต์อื่น ๆ เนื่องจากงานในสคริปต์ที่สร้างขึ้นจะทำงานที่ใดก็ได้ตั้งแต่ 0 ถึงnครั้งในเครื่องอื่นและข้อมูลที่สร้างขึ้นอาจเปลี่ยนแปลงก่อนที่จะทำงาน (อีกครั้ง) ดังนั้นการดำเนินการโดยตรงผ่านเครือข่ายจะ ไม่ทำงาน.

รับทราบตัวแปรที่อาจมีตัวอักษรพิเศษเช่นคำพูดเดียวฉันต้องเขียนออกมาเป็นตัวอักษรสตริงหนีเต็มอย่างเช่นตัวแปรfooที่มีbar'bazควรจะปรากฏในสคริปต์ที่สร้างขึ้นเป็น:

qux='bar'\''baz'

ซึ่งจะเขียนโดยการต่อท้าย"qux=$foo_esc"บรรทัดอื่นของสคริปต์ ฉันใช้ Perl แบบนี้:

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

แต่ดูเหมือนว่าเกินความจริง

ฉันไม่ประสบความสำเร็จในการทำมันด้วยการทุบตีคนเดียว ฉันได้ลองหลายแบบแล้ว:

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

แต่เครื่องหมายสแลชพิเศษอาจปรากฏในเอาต์พุต (เมื่อฉันทำecho "$foo") หรือพวกเขาทำให้เกิดข้อผิดพลาดทางไวยากรณ์ (ต้องการอินพุตเพิ่มเติมหากทำจากเชลล์)


aliasและ / หรือsetค่อนข้างเป็นสากล
mikeserv

@mikeserv ขออภัยฉันไม่แน่ใจว่าคุณหมายถึงอะไร
วอลฟอล

alias "varname=$varname" varnameหรือvar=value set
mikeserv

2
@ ไมค์เซอร์นั่นไม่เพียงพอที่ฉันจะเข้าใจว่าข้อเสนอแนะของคุณควรจะทำยังไงและฉันจะใช้มันเป็นวิธีการทั่วไปในการหลีกเลี่ยงตัวแปรใด ๆ มันเป็นปัญหาที่แก้ไขแล้ว
วอล

คำตอบ:


27

Bash มีตัวเลือกการขยายพารามิเตอร์สำหรับกรณีนี้ :

${parameter@Q}การขยายตัวเป็นสตริงที่เป็นค่าของพารามิเตอร์ที่ยกมาในรูปแบบที่สามารถนำมาใช้ใหม่เป็นอินพุต

ดังนั้นในกรณีนี้:

foo_esc="${foo@Q}"

สิ่งนี้ได้รับการสนับสนุนใน Bash 4.4 ขึ้นไป มีตัวเลือกหลายตัวสำหรับการขยายรูปแบบอื่น ๆ เช่นกันและสำหรับการสร้างคำสั่งการมอบหมายที่สมบูรณ์ ( @A) โดยเฉพาะ


7
เรียบร้อย แต่มีเพียง 4.2 bad substitutionซึ่งจะช่วยให้
Walf

3
Z เชลล์เทียบเท่า"${foo:q}"กัน
JdeBP

ช่วยชีวิตฉันจริงๆ! "${foo@Q}"ทำงาน!
hao

@JdeBP ที่ Z เชลล์เทียบเท่าใช้งานไม่ได้ มีความคิดอื่นสำหรับ zsh อีกไหม?
Steven Shaw

1
ฉันพบคำตอบ: "$ {(@ qq) foo}"
Steven Shaw

11

Bash จัดให้มีprintfbuiltin พร้อมตัว%qระบุรูปแบบซึ่งดำเนินการหลบหนีเชลล์ให้คุณแม้ในเวอร์ชันเก่ากว่า (<4.0) ของ 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\`]

เคล็ดลับนี้ยังสามารถใช้เพื่อส่งคืนอาร์เรย์ของข้อมูลจากฟังก์ชัน:

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\ \)]

โปรดทราบว่า Bash printfbuiltin นั้นแตกต่างจากprintfยูทิลิตี้ที่มาพร้อมกับระบบปฏิบัติการ Unix หากด้วยเหตุผลบางอย่างprintfคำสั่งจะเรียกใช้ยูทิลิตี้แทน builtin คุณสามารถดำเนินการbuiltin printfแทนได้เสมอ


ผมไม่แน่ใจว่าวิธีการที่จะช่วยให้ถ้าสิ่งที่ฉันจะต้องพิมพ์จะ'Ne'\''er do well'เป็นต้นคำพูดเช่นรวมในการส่งออก
Walf

1
@ วอลล์ฉันคิดว่าคุณไม่เข้าใจว่าทั้งสองรูปแบบนั้นเท่ากันและทั้งสองแบบนั้นปลอดภัยเหมือนกัน เช่น[[ 'Ne'\''er do well' == Ne\'er\ do\ well ]] && echo 'equivalent!'จะสะท้อนequivalent!
Dejay Clayton

ฉันพลาดไปแล้ว: P แต่ฉันชอบแบบฟอร์มที่ยกมาเพราะมันง่ายต่อการอ่านในวิวเวอร์ / ตัวแก้ไขไฮไลต์ของไวยากรณ์
Walf

@ วอลดูเหมือนว่าวิธีการของคุณค่อนข้างอันตรายเพราะในตัวอย่างของคุณ Perl การส่งผ่านค่าเช่น'hello'ผลลัพธ์ในค่าที่ไม่ถูกต้อง''\''hello''ซึ่งมีสตริงว่างนำที่ไม่จำเป็น
Dejay Clayton

1
@Walf สำหรับการชี้แจง$'escape-these-chars'เป็นคุณลักษณะการอ้างอิง ANSI-Cของ Bash ที่ทำให้อักขระทั้งหมดภายในสตริงที่ระบุถูก Escape ดังนั้นเพื่อสร้างตัวอักษรสตริงที่มีการขึ้นบรรทัดใหม่ภายในชื่อไฟล์ได้ง่าย (เช่น$'first-line\nsecond-line')ใช้\nภายในโครงสร้างนี้
Dejay Clayton

8

ฉันเดาว่าฉันไม่ได้ RTFM สามารถทำได้เช่นนั้น:

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

จากนั้นecho "$foo_esc"ให้ความคาดหวัง'bar'\''baz'


ฉันใช้มันอย่างไรกับฟังก์ชั่น:

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

...

foo_esc="`esc_var "$foo"`"

แก้ไขสิ่งนี้เพื่อใช้printfบิวด์อินจากโซลูชันของ Dejay:

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

3
ฉันจะใช้โซลูชัน @ michael-homer ถ้ารุ่นของฉันรองรับ
Walf

4

มีหลายวิธีในการเสนอราคาค่า var:

  1. alias
    ในเชลล์ส่วนใหญ่ (ที่ alias พร้อมใช้งาน) (ยกเว้น csh, tcsh และอื่น ๆ เช่น csh):

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

    ใช่มันใช้งานได้กับshเปลือกหอยหลายอันเช่นเส้นประหรือเถ้า

  2. ตั้งค่า
    ในเชลล์ส่วนใหญ่ (อีกครั้งไม่ใช่ csh):

    $ qux=bar\'baz
    $ set | grep '^qux='
    qux='bar'\''baz'
  3. typeset
    ในบางเชลล์ (ksh, bash และ zsh อย่างน้อย):

    $ qux=bar\'baz
    $ typeset -p qux
    typeset qux='bar'\''baz'             # this is zsh, quoting style may
                                         # be different for other shells.
  4. ส่งออก
    ก่อนทำ:

    export qux=bar\'baz

    จากนั้นใช้:
    export -p | grep 'qux=' export -p | grep 'qux='
    export -p qux

  5. quote
    echo "${qux@Q}"
    echo "${(qq)qux}" # จากหนึ่งถึงสี่ของ q สามารถใช้ได้


วิธีนามแฝงนั้นฉลาดและดูเหมือนว่ามันจะถูกระบุโดย POSIX สำหรับการพกพาสูงสุดฉันคิดว่านี่เป็นวิธีที่จะไป ฉันเชื่อว่าคำแนะนำที่เกี่ยวข้องgrepกับexportหรือsetอาจแตกหักตัวแปรที่มีบรรทัดใหม่ฝังอยู่
jw013
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.