ความแตกต่างระหว่างคำพูดเดี่ยวและคู่ใน Bash


569

ใน Bash ความแตกต่างระหว่างเครื่องหมายคำพูดเดี่ยว ( '') และเครื่องหมายคำพูดคู่ ( "") คืออะไร?


1
ดูเพิ่มเติม (ไซต์ที่อาจเป็นข้อมูลซ้ำซ้อน): คำพูด“ …”, '…', $ '…' และ $“ …” แตกต่างกันอย่างไร? จากUnix และ Linux Stack แลกเปลี่ยน
rugk

คำตอบ:


579

เครื่องหมายคำพูดเดี่ยวจะไม่สอดแทรกอะไร แต่เครื่องหมายคำพูดคู่จะ ตัวอย่างเช่น: ตัวแปรแบคทีก, \หนีบางอย่างฯลฯ

ตัวอย่าง:

$ echo "$(echo "upg")"
upg
$ echo '$(echo "upg")'
$(echo "upg")

คู่มือ Bash มีสิ่งนี้ที่จะกล่าวว่า:

3.1.2.2 Single Quotes

การใส่อักขระในเครื่องหมายคำพูดเดี่ยว ( ') จะเก็บค่าตามตัวอักษรของแต่ละอักขระไว้ในเครื่องหมายคำพูด เครื่องหมายคำพูดเดี่ยวอาจไม่เกิดขึ้นระหว่างเครื่องหมายคำพูดเดี่ยวแม้ว่าจะนำหน้าด้วยเครื่องหมายทับขวา

3.1.2.3 เครื่องหมายคำพูดคู่

ล้อมรอบตัวอักษรในคำพูดคู่ ( ") เก็บรักษามูลค่าที่แท้จริงของตัวละครทุกตัวที่อยู่ในคำพูดที่มีข้อยกเว้นของ$, `, และเมื่อการขยายตัวประวัติถูกเปิดใช้งาน\ !ตัวละคร$และ`รักษาความหมายพิเศษไว้ในเครื่องหมายคำพูดคู่ (ดูการขยายเชลล์ ) เครื่องหมายยังคงมีความหมายพิเศษเฉพาะเมื่อตามด้วยหนึ่งในตัวละครต่อไปนี้: $, `, ",\หรือขึ้นบรรทัดใหม่ ภายในเครื่องหมายคำพูดคู่เครื่องหมายแบ็กสแลชที่ตามด้วยหนึ่งในอักขระเหล่านี้จะถูกลบออก แบ็กสแลชที่นำหน้าอักขระที่ไม่มีความหมายพิเศษจะไม่มีการแก้ไข เครื่องหมายคำพูดคู่อาจถูกอ้างอิงภายในเครื่องหมายคำพูดคู่โดยนำหน้าด้วยเครื่องหมายทับขวา หากเปิดใช้งานการขยายประวัติจะดำเนินการเว้นแต่ว่า!เครื่องหมายอัญประกาศคู่ปรากฏขึ้นโดยใช้เครื่องหมายทับขวา แบ็กสแลชก่อนหน้า!จะไม่ถูกลบออก

พารามิเตอร์พิเศษ*และ@มีความหมายพิเศษเมื่ออยู่ในเครื่องหมายคำพูดคู่ (ดูการขยายพารามิเตอร์ของเชลล์ )


41
สำหรับใครก็ตามที่ไม่ทราบว่า "การสอดแทรก" หมายถึง: en.wikipedia.org/wiki/String_interpolation
Kolob Canyon

สิ่งที่เกี่ยวกับเมื่อคุณกำลังใช้git_promptที่คอมไพล์ให้พวกเขาขอแนะนำให้ใช้มันเช่นนี้PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ ', คอมไพล์พรอมต์ตามนี้ไม่ควรทำงาน มีบางสิ่งที่พิเศษเกี่ยวกับPS#ตัวแปรหรือไม่? หรือทำไมมันทำงานถ้ามันไม่ได้ทำการแก้ไข
ekiim

@ekiim ว่าข้อความที่แน่นอนมีการตั้งค่า (ไม่เปลี่ยนแปลง) PS1ลง ลองecho $PS1ดูสิ่งที่ฉันหมายถึง แต่PS1จะถูกประเมินก่อนแสดง (ดูPROMPTINGหัวข้อใน bash manpage) PS1='$X'การทดสอบนี้ลอง คุณจะไม่มีพรอมต์ จากนั้นเรียกใช้X=fooและทันใดนั้นข้อความแจ้งของคุณคือ "foo" (ได้PS1รับการประเมินเมื่อตั้งค่าแทนที่จะแสดงคุณจะยังไม่มีข้อความแจ้ง)
Adam Batkin

262

ตอบรับดีมาก ฉันกำลังจัดทำตารางที่ช่วยให้เข้าใจหัวข้อได้อย่างรวดเร็ว คำอธิบายที่เกี่ยวข้องกับตัวแปรที่เรียบง่ายเช่นเดียวกับอาร์เรย์จัดทำดัชนีaarr

หากเราตั้งไว้

a=apple      # a simple variable
arr=(apple)  # an indexed array with a single element

แล้วechoนิพจน์ในคอลัมน์ที่สองเราจะได้ผลลัพธ์ / พฤติกรรมที่แสดงในคอลัมน์ที่สาม คอลัมน์ที่สี่อธิบายพฤติกรรม

 # | Expression  | Result      | Comments
---+-------------+-------------+--------------------------------------------------------------------
 1 | "$a"        | apple       | variables are expanded inside ""
 2 | '$a'        | $a          | variables are not expanded inside ''
 3 | "'$a'"      | 'apple'     | '' has no special meaning inside ""
 4 | '"$a"'      | "$a"        | "" is treated literally inside ''
 5 | '\''        | **invalid** | can not escape a ' within ''; use "'" or $'\'' (ANSI-C quoting)
 6 | "red$arocks"| red         | $arocks does not expand $a; use ${a}rocks to preserve $a
 7 | "redapple$" | redapple$   | $ followed by no variable name evaluates to $
 8 | '\"'        | \"          | \ has no special meaning inside ''
 9 | "\'"        | \'          | \' is interpreted inside "" but has no significance for '
10 | "\""        | "           | \" is interpreted inside ""
11 | "*"         | *           | glob does not work inside "" or ''
12 | "\t\n"      | \t\n        | \t and \n have no special meaning inside "" or ''; use ANSI-C quoting
13 | "`echo hi`" | hi          | `` and $() are evaluated inside ""
14 | '`echo hi`' | `echo hi`   | `` and $() are not evaluated inside ''
15 | '${arr[0]}' | ${arr[0]}   | array access not possible inside ''
16 | "${arr[0]}" | apple       | array access works inside ""
17 | $'$a\''     | $a'         | single quotes can be escaped inside ANSI-C quoting
18 | "$'\t'"     | $'\t'       | ANSI-C quoting is not interpreted inside ""
19 | '!cmd'      | !cmd        | history expansion character '!' is ignored inside ''
20 | "!cmd"      | cmd args    | expands to the most recent command matching "cmd"
21 | $'!cmd'     | !cmd        | history expansion character '!' is ignored inside ANSI-C quotes
---+-------------+-------------+--------------------------------------------------------------------

ดูสิ่งนี้ด้วย:


1
คำตอบที่ได้รับการยอมรับบอกว่าในท้ายที่สุดThe special parameters * and @ have special meaning when in double quotesแล้วทำไมจึงเกิด"*"ผลลัพธ์ขึ้นมา*?
anddero

2
@ Karl-AnderoMere เพราะพวกเขาไม่ได้ขยายเป็นพารามิเตอร์ในกรณีนั้นเลย "$@"และ"$*"เป็นการขยายพารามิเตอร์ "@"และ"*"ไม่ใช่
Charles Duffy

@CharlesDuffy ขอบคุณมันเข้าท่าแล้ว!
anddero

3
จำนวน 9 กลับฉันecho "\'" \'
MaxGyver

@ MaxGyver: ขอบคุณที่ชี้ไป ฉันได้อัพเดตคำตอบแล้ว
codeforester

233

หากคุณอ้างถึงสิ่งที่เกิดขึ้นเมื่อคุณสะท้อนบางสิ่งเครื่องหมายคำพูดเดียวจะสะท้อนสิ่งที่คุณมีระหว่างพวกเขาในขณะที่เครื่องหมายคำพูดคู่จะประเมินตัวแปรระหว่างพวกเขาและส่งออกค่าของตัวแปร

ตัวอย่างเช่นสิ่งนี้

#!/bin/sh
MYVAR=sometext
echo "double quotes gives you $MYVAR"
echo 'single quotes gives you $MYVAR'

จะให้สิ่งนี้:

double quotes gives you sometext
single quotes gives you $MYVAR

11

คนอื่นอธิบายได้ดีมากและเพียงแค่ต้องการยกตัวอย่างง่ายๆ

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

$ echo 'All sorts of things are ignored in single quotes, like $ & * ; |.' 

มันจะให้สิ่งนี้:

All sorts of things are ignored in single quotes, like $ & * ; |.

สิ่งเดียวที่ไม่สามารถใส่ในเครื่องหมายคำพูดเดียวคือเครื่องหมายคำพูดเดี่ยว

เครื่องหมายคำพูดคู่ทำหน้าที่คล้ายกับเครื่องหมายคำพูดเดี่ยวยกเว้นเครื่องหมายคำพูดคู่ยังอนุญาตให้เชลล์ตีความเครื่องหมายดอลลาร์เครื่องหมายคำพูดหลังและแบ็กสแลช เป็นที่ทราบกันแล้วว่าแบ็กสแลชป้องกันไม่ให้มีการตีความอักขระพิเศษเพียงตัวเดียว สิ่งนี้มีประโยชน์ในเครื่องหมายคำพูดคู่หากจำเป็นต้องใช้เครื่องหมายดอลลาร์เป็นข้อความแทนที่จะเป็นตัวแปร นอกจากนี้ยังอนุญาตให้ใช้อัญประกาศคู่เพื่อไม่ให้ถูกตีความว่าเป็นจุดสิ้นสุดของสตริงที่ยกมา

$ echo "Here's how we can use single ' and double \" quotes within double quotes"

มันจะให้สิ่งนี้:

Here's how we can use single ' and double " quotes within double quotes

อาจสังเกตได้ว่าเครื่องหมายอัญประกาศเดี่ยวซึ่งจะตีความได้ว่าเป็นจุดเริ่มต้นของสตริงที่ยกมานั้นจะถูกละเว้นภายในเครื่องหมายคำพูดคู่ อย่างไรก็ตามตัวแปรถูกตีความและทดแทนด้วยค่าภายในเครื่องหมายคำพูดคู่

$ echo "The current Oracle SID is $ORACLE_SID"

มันจะให้สิ่งนี้:

The current Oracle SID is test

เครื่องหมายคำพูดย้อนกลับไม่เหมือนคำพูดเดียวหรือสองคำ แทนที่จะใช้เพื่อป้องกันการตีความตัวอักษรพิเศษเครื่องหมายคำพูดด้านหลังบังคับให้มีการดำเนินการตามคำสั่งที่แนบมาด้วย หลังจากรันคำสั่งที่ล้อมรอบเอาต์พุตของพวกเขาจะถูกแทนที่ด้วยเครื่องหมายคำพูดด้านหลังในบรรทัดต้นฉบับ นี่จะชัดเจนขึ้นด้วยตัวอย่าง

$ today=`date '+%A, %B %d, %Y'`
$ echo $today 

มันจะให้สิ่งนี้:

Monday, September 28, 2015 

3

มีความแตกต่างที่ชัดเจนระหว่างการใช้งานของเป็นและ' '" "

เมื่อ' 'ใช้กับสิ่งใดก็ตามจะไม่มี "การแปลงหรือการแปล" ทำ มันถูกพิมพ์ตามที่เป็น

ด้วย" "สิ่งที่มันล้อมรอบคือ "แปลหรือแปลง" เป็นค่าของมัน

โดยการแปล / การแปลงฉันหมายถึงสิ่งต่อไปนี้: สิ่งใดก็ตามที่อยู่ในเครื่องหมายคำพูดเดียวจะไม่ถูก "แปล" เป็นค่าของพวกเขา พวกเขาจะต้องดำเนินการตามที่พวกเขาอยู่ในคำพูด ตัวอย่าง: a=23จากนั้นecho '$a'จะสร้าง$aเอาต์พุตมาตรฐาน ในขณะที่echo "$a"จะผลิต23ออกมาตรฐาน


1
คุณแปลว่า "การแปล" หรือ "การเปลี่ยนแปลง" หมายความว่าอะไร?
Nico Haase

คำตอบนี้ค่อนข้างสับสนและไม่ได้เพิ่มอะไรเลยกับคำตอบที่ดีที่มีอยู่
codeforester

2
นี่เป็นคำตอบสั้น ๆ สั้น ๆ ในความเห็นของฉันโดยที่ไม่ต้องพูดมากจนเกินไปที่จะเข้าใจได้ง่าย เมื่อพูดการแปล / การแปลงพวกเขาหมายถึงเครื่องหมายคำพูดคู่จะขยายตัวแปรที่คำพูดเดียวจะไม่ขยายตัวแปร
B_e_n_n_y_

1
ใช่นี่คือคำตอบที่ดีที่สุด นี่ควรเป็นคำตอบที่ยอมรับได้
Jamey Kirby

3

เนื่องจากนี่คือคำตอบที่แท้จริงเมื่อจัดการกับคำพูดในbashฉันจะเพิ่มจุดอีกพลาดในคำตอบข้างต้นเมื่อจัดการกับตัวดำเนินการทางคณิตศาสตร์ในเปลือก

bashเปลือกสนับสนุนทั้งสองวิธีการทำดำเนินการทางคณิตศาสตร์อย่างใดอย่างหนึ่งที่กำหนดโดยในตัวletคำสั่งและ$((..))ผู้ประกอบการ อดีตประเมินการแสดงออกทางคณิตศาสตร์ในขณะที่หลังมีมากกว่าคำสั่งผสม

สิ่งสำคัญคือต้องเข้าใจว่านิพจน์ทางคณิตศาสตร์ที่ใช้กับletการแยกคำการขยายชื่อพา ธ เหมือนกับคำสั่งเชลล์อื่น ๆ การอ้างและการหลบหนีที่เหมาะสมจะต้องทำ

ดูตัวอย่างนี้เมื่อใช้ let

let 'foo = 2 + 1'
echo $foo
3

การใช้เครื่องหมายอัญประกาศเดี่ยวที่นี่ก็ใช้ได้จริงที่นี่เนื่องจากไม่จำเป็นต้องขยายตัวแปรที่นี่พิจารณากรณีของ

bar=1
let 'foo = $bar + 1'

จะล้มเหลวอย่างน่าสังเวชเช่น $barเครื่องหมายคำพูดเดี่ยวภายใต้จะไม่ขยายและจำเป็นต้องมีเครื่องหมายคำพูดคู่เป็น

let 'foo = '"$bar"' + 1'

นี่ควรเป็นหนึ่งในเหตุผลที่$((..))ควรพิจารณามากกว่าการใช้letควรได้รับการพิจารณามากกว่าการใช้เนื่องจากภายในเนื้อหาไม่ได้ขึ้นอยู่กับการแยกคำ ตัวอย่างก่อนหน้านี้การใช้letสามารถเขียนเป็น

(( bar=1, foo = bar + 1 ))

อย่าลืมใช้$((..))โดยไม่ต้องใส่เครื่องหมายคำพูดเดียว

แม้ว่า$((..))สามารถใช้กับเครื่องหมายคำพูดคู่ได้ แต่ก็ไม่มีจุดประสงค์เนื่องจากผลลัพธ์ไม่สามารถทำได้มีเนื้อหาที่ต้องมีเครื่องหมายคำพูดคู่ เพียงตรวจสอบให้แน่ใจว่ามันไม่ได้ยกมาเดี่ยว

printf '%d\n' '$((1+1))'
-bash: printf: $((1+1)): invalid number
printf '%d\n' $((1+1))
2
printf '%d\n' "$((1+1))"
2

อาจเป็นกรณีพิเศษบางประการของการใช้ตัว$((..))ดำเนินการภายในสตริงที่ยกมาเดี่ยวคุณต้องแทรกเครื่องหมายอัญประกาศในลักษณะที่ตัวดำเนินการอาจไม่ได้ใส่เครื่องหมายคำพูดหรือเครื่องหมายอัญประกาศ เช่นพิจารณากรณีเมื่อคุณคาดว่าจะใช้ตัวดำเนินการภายในcurlคำสั่งเพื่อส่งต่อตัวนับทุกครั้งที่มีการร้องขอให้ทำ

curl http://myurl.com --data-binary '{"requestCounter":'"$((reqcnt++))"'}'

สังเกตเห็นการใช้เครื่องหมายคำพูดคู่ซ้อนภายในโดยที่สตริงตัวอักษร$((reqcnt++))ถูกส่งผ่านไปยังrequestCounterเขตข้อมูล


2
Charles Duffyทำให้เป็นกรณีที่ดีที่นี่สำหรับการอ้างอิงสองครั้ง$((...))เช่นกัน อาจเป็น "หวาดระแวง" เล็กน้อยและไม่น่าเป็นไปได้ที่จะIFS=0เป็นตัวอย่าง แต่แน่นอนว่ามันเป็นไปไม่ได้อย่างแน่นอน :)
24432

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