ฉันจะหลีกเลี่ยงคำพูดซ้ำภายในเครื่องหมายคำพูดคู่ได้อย่างไร


287

ฉันจะหลีกเลี่ยงอัญประกาศคู่ภายในสตริงคู่ใน Bash ได้อย่างไร

ตัวอย่างเช่นในเชลล์สคริปต์ของฉัน

#!/bin/bash

dbload="load data local infile \"'gfpoint.csv'\" into table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY \"'\n'\" IGNORE 1 LINES"

ฉันไม่สามารถใช้เครื่องหมายENCLOSED BY '\"'คำพูดคู่เพื่อหลบหนีได้อย่างถูกต้อง $dbtableฉันไม่สามารถใช้คำพูดเดียวสำหรับตัวแปรของฉันเพราะฉันต้องการที่จะใช้ตัวแปร




2
@kenorb ดูเหมือนจะไม่เหมือนกันกับคำถามนั้น ...
ทำเครื่องหมาย


@Daenyth นี่ไม่ใช่คำสั่งประเภทที่คุณคาดหวังให้ผู้ใช้ปลายทางเข้าถึงได้ สคริปต์โหลดจำนวนมากมักจะทำงานบนเซิร์ฟเวอร์โดยผู้ใช้ที่เชื่อถือได้ (เช่นผู้ดูแลระบบหรือนักพัฒนา) ใช่ถ้าผู้ใช้ควบคุมค่าได้$dbtableมีความเสี่ยง นี่เป็นเรื่องแปลกมากเนื่องจากผู้ใช้ทั่วไปมักจะไม่ใช้ SSH ในเครื่องเพื่อโหลดข้อมูล
jpmc26

คำตอบ:


285

ใช้แบ็กสแลช:

echo "\""     # Prints one " character.

9
ไม่ทำงาน. x=ls; if [ -f "$(which "\""$x"\"")" ]; then echo exists; else echo broken; fi;ให้หักในขณะที่... [ -f "$(which $x)" ]; ...หรือ... [ -f $(which "$x") ]; ...ทำงานได้ดี ปัญหาจะเกิดขึ้นเมื่อใด$xหรือผลของการ$(which "$x")ให้อะไรกับช่องว่างหรืออักขระพิเศษอื่น ๆ วิธีแก้ปัญหาคือการใช้ตัวแปรเพื่อเก็บผลลัพธ์whichแต่ทุบตีไม่สามารถหลบหนีจากคำพูดหรือฉันทำอะไรผิดหรือเปล่า?
Luc

ฉันพยายามใช้ต่อไปนี้grep -oh "\"\""$counter"\""\w*" เป็นส่วนหนึ่งของ bash syntax ซึ่งใน$counterนั้นเป็นตัวแปร ไม่ชอบความคิดใด ๆ
Jay D

82

ตัวอย่างง่ายๆของการหลีกเลี่ยงคำพูดในเชลล์:

$ echo 'abc'\''abc'
abc'abc
$ echo "abc"\""abc"
abc"abc

ทำได้โดยการเปิดหนึ่ง ( ') ที่เปิดอยู่แล้วให้วางหนึ่งที่หลบหนี ( \') แล้วเปิดอีกอันหนึ่ง ( ')

อีกวิธีหนึ่งคือ:

$ echo 'abc'"'"'abc'
abc'abc
$ echo "abc"'"'"abc"
abc"abc

ดำเนินการเสร็จแล้วเปิดหนึ่ง ( ') วางใบเสนอราคาในใบเสนอราคาอื่น ( "'") แล้วเปิดอีกหนึ่งใบ ( ')

ตัวอย่างเพิ่มเติม: การหลีกเลี่ยงอัญประกาศเดี่ยวภายในสตริงที่ยกมาเดี่ยว


1
ฉันพยายาม sh -c "echo '{" key ":" value "}'" และแม้แต่ sh -c "echo '{' '' '' คีย์ '' '' ':' '' '' 'ค่า' '' '' ' '}' "ในความพยายามที่จะใส่คีย์คำและค่าในเครื่องหมายคำพูดคู่ แต่ในทั้งสองกรณีฉันได้รับ {key: value}
Igor Yagolnitser

1
ดูเหมือนจะซับซ้อนโดยไม่จำเป็นสำหรับเครื่องหมายคำพูดคู่: echo "abc\"abc"เพียงพอที่จะสร้างabc"abcตามคำตอบของปีเตอร์
divenex

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

63

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

ตัวอย่าง:

 echo -e "This is \x22\x27\x22\x27\x22text\x22\x27\x22\x27\x22"
 This is "'"'"text"'"'"

\x22 คือรหัส ASCII (เป็นเลขฐานสิบหก) สำหรับเครื่องหมายคำพูดคู่และ \x27สำหรับเครื่องหมายคำพูดเดี่ยว ในทำนองเดียวกันคุณสามารถสะท้อนอักขระใด ๆ

ฉันคิดว่าถ้าเราพยายามที่จะสะท้อนสตริงข้างต้นด้วยแบ็กสแลชเราจะต้องมีแบ็กสแลชสองแถวที่ยุ่งเหยิง ... :)

สำหรับการกำหนดตัวแปรนี่เทียบเท่า:

 $ a=$'This is \x22text\x22'
 $ echo "$a"
 This is "text"

หากตัวแปรนั้นถูกตั้งค่าโดยโปรแกรมอื่นแล้วคุณยังสามารถใช้เครื่องหมายคำพูดคู่ / เดี่ยวกับ sed หรือเครื่องมือที่คล้ายกันได้

ตัวอย่าง:

 $ b="Just another text here"
 $ echo "$b"
 Just another text here
 $ sed 's/text/"'\0'"/' <<<"$b" #\0 is a special sed operator
 Just another "0" here #this is not what i wanted to be
 $ sed 's/text/\x22\x27\0\x27\x22/' <<<"$b"
 Just another "'text'" here #now we are talking. You would normally need a dozen of backslashes to achieve the same result in the normal way.

1
+1 เพราะมันแก้ปัญหาการเพิ่มตัวแปร PS1 ให้กับ ~ / echo 'export PS1='\[\033[00;31m\]${?##0}$([ $? -ne 0 ] && echo \x22 \x22)\[\033[00;32m\]\u\[\033[00m\]@\[\033[00;36m\]\h\[\033[00m\][\[\033[01;33m\]\d \t\[\033[00m\]] \[\033[01;34m\]\w\n\[\033[00m\]$( [ ${EUID} -ne 0 ] && echo \x22$\x22 || echo \x22#\x22 ) '' >> ~/.profile
.profile

1
นี่คือคำตอบ! ฉันรักคุณครับ
Miguel Rojas Cortés

28

Bash ช่วยให้คุณวางสายติดกันและพวกเขาก็จะถูกจับเข้าด้วยกัน

ดังนั้นนี่คือ:

$ echo "Hello"', world!'

ผลิต

Hello, world!

เคล็ดลับคือการสลับระหว่างสตริงเดี่ยวและคู่ที่ยกมาตามที่ต้องการ น่าเสียดายที่มันยุ่งมากอย่างรวดเร็ว ตัวอย่างเช่น:

$ echo "I like to use" '"double quotes"' "sometimes"

ผลิต

I like to use "double quotes" sometimes

ในตัวอย่างของคุณฉันจะทำสิ่งนี้:

$ dbtable=example
$ dbload='load data local infile "'"'gfpoint.csv'"'" into '"table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '"'"'"' LINES "'TERMINATED BY "'"'\n'"'" IGNORE 1 LINES'
$ echo $dbload

ซึ่งสร้างผลลัพธ์ต่อไปนี้:

load data local infile "'gfpoint.csv'" into table example FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY "'\n'" IGNORE 1 LINES

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

dbload=' load data local infile "' 'gfpoint.csv'' " into' table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '' "' ' LINES' TERMINATED BY "' '\n'' " IGNORE 1 LINES' '' '' '' '' ''

เครื่องหมายคำพูดเช่น "''" ด้านบนจะถูกตีความโดย bash เครื่องหมายคำพูด" 'จะสิ้นสุดในตัวแปรผลลัพธ์

หากฉันให้การรักษาแบบเดียวกันกับตัวอย่างก่อนหน้านี้ดูเหมือนว่า:

$ echoI like to use' "double quotes"' sometimes


4
Oof ... นั่นเป็นสิ่งที่น่าเกลียด
Magic Octopus Urn

17

ตรวจสอบprintf ...

#!/bin/bash
mystr="say \"hi\""

โดยไม่ต้องใช้ printf

echo -e $mystr

ผลลัพธ์: พูดว่า "สวัสดี"

ใช้งานprintf

echo -e $(printf '%q' $mystr)

ผลลัพธ์: พูด \ "สวัสดี \"


2
โปรดทราบว่าprintfหนีตัวละครมากขึ้นเช่นกันเช่น', (และ)
เดวิดPärsson

printf %qสร้างสตริงพร้อมสำหรับการไม่ได้จัดรูปแบบสำหรับeval echo -e
Charles Duffy

2
ไม่มีเหตุผลที่จะห่อไม่เป็นprintfที่มีการใช้งานที่ไร้ประโยชน์ของ echoทั้งตัวอย่างของคุณมีการอ้างอิงที่ไม่สมบูรณ์ การแก้ไขที่เหมาะสมคือการอ้างอิงตัวแปร
tripleee

15

เก็บอักขระเครื่องหมายคำพูดคู่เป็นตัวแปร:

dqt = '"'
echo "เครื่องหมายคำพูดคู่ $ {dqt} X $ {dqt} ภายในสตริงที่มีเครื่องหมายคำพูดคู่"

เอาท์พุท:

เครื่องหมายคำพูดคู่ "X" ภายในสตริงที่มีเครื่องหมายคำพูดคู่

39
Bash เป็นภาษาที่เลวร้ายที่สุดอย่างแท้จริง
Andy Ray

@ 12oclocker คำตอบของคุณไร้สาระ: D! พิเศษเมื่อใช้กับคำสั่ง "sed" มันช่วยชีวิตฉันไว้!
Artanis Zeratul

11

ใช้ประโยชน์จาก $ "สตริง"

ในตัวอย่างนี้มันจะเป็น

dbload = $ "โหลด data local infile \" 'gfpoint.csv' \ "ลงในตาราง $ dbtable FI สิ้นสุดลงตาม ',' ENCLOSED โดย '\"' LINES สิ้นสุดโดย \ "'\ n' \" IGNORE 1 LINES "

หมายเหตุ (จากหน้าคน ):

สตริงที่ยกมาสองครั้งนำหน้าด้วยเครื่องหมายดอลลาร์ ($ "สตริง") จะทำให้สตริงที่จะแปลตามสถานที่ปัจจุบัน หากโลแคลปัจจุบันคือ C หรือ POSIX เครื่องหมายดอลลาร์จะถูกละเว้น หากสตริงถูกแปลและแทนที่การแทนที่จะเป็นการเสนอราคาสองครั้ง


3
ดีไม่รู้ว่าอย่างนั้น
David Kierans

-5

เพิ่ม"\"ก่อนอัญประกาศเพื่อหลีกเลี่ยงมันแทน\

#! /bin/csh -f

set dbtable = balabala

set dbload = "load data local infile "\""'gfpoint.csv'"\"" into table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '"\""' LINES TERMINATED BY "\""'\n'"\"" IGNORE 1 LINES"

echo $dbload
# load data local infile "'gfpoint.csv'" into table balabala FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY "''" IGNORE 1 LINES

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