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


1016

สมมติว่าคุณมี Bash aliasเช่น:

alias rxvt='urxvt'

ซึ่งใช้งานได้ดี

อย่างไรก็ตาม:

alias rxvt='urxvt -fg '#111111' -bg '#111111''

จะไม่ทำงานและจะไม่:

alias rxvt='urxvt -fg \'#111111\' -bg \'#111111\''

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

alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''

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


16
คุณรู้หรือไม่ว่าคุณไม่จำเป็นต้องใช้เครื่องหมายคำพูดเดี่ยวสำหรับนามแฝง คำพูดคู่นั้นง่ายกว่ามาก
teknopaul

4
ดูเพิ่มเติม: ความแตกต่างระหว่างคำพูดเดี่ยวและคู่ในทุบตี
codeforester

3
เครื่องหมายคำพูดซ้อนซ้อนนั้นสามารถหลีกเลี่ยงได้"\""ดังนั้นควรใช้เครื่องหมายอัญประกาศคู่กับ @ liori ของคำตอบทุกครั้งที่ทำได้
ลัน

7
เครื่องหมายคำพูดคู่นั้นมีความแตกต่างจากเครื่องหมายคำพูดเดี่ยวใน * ระวัง (รวมถึง Bash และเครื่องมือที่เกี่ยวข้องเช่น Perl) ดังนั้นการแทนที่เครื่องหมายคำพูดคู่เมื่อใดก็ตามที่มีปัญหากับเครื่องหมายคำพูดเดี่ยวจะไม่เป็นทางออกที่ดี เครื่องหมายคำพูดคู่ระบุ $ ... ตัวแปรที่จะถูกแทนที่ก่อนที่จะดำเนินการในขณะที่คำพูดเดียวระบุ $ ... จะได้รับการปฏิบัติอย่างแท้จริง
Chuck Kollars

หากคุณคิดว่าฉันใช้เครื่องหมายคำพูดคู่ แต่ก็ยังไม่ทำงานให้ส่งสคริปต์ของคุณอีกครั้ง
Samy Bencherif

คำตอบ:


1454

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

 alias rxvt='urxvt -fg '"'"'#111111'"'"' -bg '"'"'#111111'"'"
 #                     ^^^^^       ^^^^^     ^^^^^       ^^^^
 #                     12345       12345     12345       1234

คำอธิบายของวิธีการ'"'"'ตีความเช่นเดียวกับ':

  1. ' สิ้นสุดใบเสนอราคาแรกซึ่งใช้เครื่องหมายคำพูดเดี่ยว
  2. " เริ่มต้นใบเสนอราคาที่สองโดยใช้เครื่องหมายคำพูดคู่
  3. ' อักขระที่ยกมา
  4. " จบใบเสนอราคาที่สองโดยใช้เครื่องหมายคำพูดคู่
  5. ' เริ่มต้นใบเสนอราคาที่สามโดยใช้เครื่องหมายคำพูดเดี่ยว

หากคุณไม่วางช่องว่างใด ๆ ระหว่าง (1) และ (2) หรือระหว่าง (4) ถึง (5) เชลล์จะตีความสตริงนั้นเป็นคำที่ยาวหนึ่งคำ


5
alias splitpath='echo $PATH | awk -F : '"'"'{print "PATH is set to"} {for (i=1;i<=NF;i++) {print "["i"]",$i}}'"'"มันทำงานได้เมื่อมีทั้งคำพูดเดียวและคำพูดสองครั้งในสตริงนามแฝง!
เนิน _ '1

17
การตีความของฉัน: ทุบตีโดยนัยเชื่อมนิพจน์สตริงที่ยกมาแตกต่างกันโดยนัย
Benjamin Atkin

2
ทำงานให้ฉันตัวอย่างของคำพูดเดียวที่หนีสองครั้ง:alias serve_this_dir='ruby -rrack -e "include Rack;Handler::Thin.run Builder.new{run Directory.new'"'"''"'"'}"'
JAMESSTONEco

2
แน่นอนว่าไม่ใช่วิธีแก้ปัญหาที่อ่านได้มากที่สุด มันใช้มากกว่าคำพูดเดียวที่พวกเขาไม่จำเป็นจริงๆ
oberlies

26
ผมยืนยันว่าเป็นอย่างมากมายอ่านได้มากขึ้นในบริบทที่มากที่สุดกว่า'\'' '"'"'ในความเป็นจริงอดีตมักจะมีความแตกต่างอย่างชัดเจนภายในสตริงที่ยกมาเดี่ยวและดังนั้นจึงเป็นเพียงเรื่องของการทำแผนที่มันความหมายกับ "มันเป็นคำพูดที่หลบหนี" ความหมายเช่นเดียวกับ\"ในสองสตริงที่ยกมา ในขณะที่ส่วนหลังผสมผสานเป็นหนึ่งในเครื่องหมายคำพูดหนึ่งบรรทัดและต้องการการตรวจสอบอย่างรอบคอบในหลาย ๆ กรณี
mtraceur

263

ฉันมักจะแทนที่แต่ละอัญประกาศเดี่ยวฝังตัวเดียวกับลำดับ: '\''(นั่นคือ: quote เครื่องหมายทับขวาอ้างคำพูดทับหลัง) ซึ่งจะปิดสตริงผนวกคำพูดเดียวที่หลบหนีและเพิ่มสตริงอีกครั้ง


ฉันมักจะชักจูงฟังก์ชั่น "อ้างอิง" ในสคริปต์ Perl ของฉันเพื่อทำสิ่งนี้ให้ฉัน ขั้นตอนจะเป็น:

s/'/'\\''/g    # Handle each embedded quote
$_ = qq['$_']; # Surround result with single quotes.

เรื่องนี้ดูแลทุกกรณี

ชีวิตจะสนุกมากขึ้นเมื่อคุณแนะนำevalเชลล์สคริปต์ของคุณ คุณจำเป็นต้องอ้างอิงทุกสิ่งอีกครั้ง!

ตัวอย่างเช่นสร้างสคริปต์ Perl ชื่อ quotify ที่มีคำสั่งด้านบน:

#!/usr/bin/perl -pl
s/'/'\\''/g;
$_ = qq['$_'];

จากนั้นใช้มันเพื่อสร้างสตริงที่เสนอราคาอย่างถูกต้อง:

$ quotify
urxvt -fg '#111111' -bg '#111111'

ผลลัพธ์:

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

ซึ่งสามารถคัดลอก / วางลงในคำสั่ง alias:

alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

(หากคุณต้องการแทรกคำสั่งลงใน Eval ให้เรียกใช้การอ้างอิงอีกครั้ง:

 $ quotify
 alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

ผลลัพธ์:

'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''

ซึ่งสามารถคัดลอก / วางลงใน eval:

eval 'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''

1
แต่นี่ไม่ใช่ Perl และเมื่อ Steve B ชี้ให้เห็นข้างต้นด้วยการอ้างอิงของเขากับ "คู่มืออ้างอิง gnu" คุณไม่สามารถหลีกเลี่ยงเครื่องหมายคำพูดใน bash ภายในเครื่องหมายคำพูดประเภทเดียวกัน และในความเป็นจริงไม่จำเป็นต้องหลบหนีพวกเขาภายในเครื่องหมายอัญประกาศเช่น "'" เป็นสตริงคำพูดเดี่ยวที่ถูกต้องและ' '' เป็นสตริงคำพูดคู่ที่ถูกต้องโดยไม่ต้องมีการหลบหนีใด ๆ
nicerobot

8
@nicerobot: ฉันได้เพิ่มตัวอย่างที่แสดงให้เห็นว่า: 1) ฉันไม่พยายามที่จะหลีกเลี่ยงคำพูดที่อยู่ในเครื่องหมายคำพูดประเภทเดียวกัน 2) หรือในทางเลือกอื่นและ 3) Perl ใช้ในการสร้างกระบวนการที่ถูกต้องโดยอัตโนมัติ สตริง bash ที่มีเครื่องหมายคำพูดฝังอยู่
Adrian Pronk

18
ย่อหน้าแรกคือคำตอบที่ฉันต้องการ
Dave Causey

9
นี่คือสิ่งที่ทุบตีไม่เป็นอย่างดีพิมพ์set -xและและคุณจะเห็นว่ารันทุบตีecho "here's a string" echo 'here'\''s a string'( set +xเพื่อกลับสู่พฤติกรรมปกติ)
arekolek

195

เนื่องจากไวยากรณ์ของBash 2.04$'string' (แทนที่จะเป็นเพียงแค่'string'คำเตือน: อย่าสับสน$('string')) เป็นกลไกการอ้างอิงอีกอันหนึ่งซึ่งอนุญาตให้ใช้ลำดับการหลบหนีแบบ ANSI C ที่เหมือนกันและขยายเป็นเวอร์ชั่นที่อ้างถึงเดี่ยว

ตัวอย่างง่ายๆ:

  $> echo $'aa\'bb'
  aa'bb

  $> alias myvar=$'aa\'bb'
  $> alias myvar
  alias myvar='aa'\''bb'

ในกรณีของคุณ:

$> alias rxvt=$'urxvt -fg \'#111111\' -bg \'#111111\''
$> alias rxvt
alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

ลำดับการหลบหนีทั่วไปทำงานตามที่คาดไว้:

\'     single quote
\"     double quote
\\     backslash
\n     new line
\t     horizontal tab
\r     carriage return

ด้านล่างเป็นสำเนา + เอกสารที่เกี่ยวข้องวางจากman bash(เวอร์ชัน 4.4):

คำของฟอร์ม 'string' ได้รับการปฏิบัติเป็นพิเศษ คำนี้ขยายออกเป็นสตริงโดยแทนที่อักขระเครื่องหมายทับขวาที่ทับหลังตามที่ระบุโดยมาตรฐาน ANSI C Backslash escape sequences ถ้ามีจะถูกถอดรหัสดังนี้:

    \a     alert (bell)
    \b     backspace
    \e
    \E     an escape character
    \f     form feed
    \n     new line
    \r     carriage return
    \t     horizontal tab
    \v     vertical tab
    \\     backslash
    \'     single quote
    \"     double quote
    \?     question mark
    \nnn   the eight-bit character whose value is the octal 
           value nnn (one to three digits)
    \xHH   the eight-bit character whose value is the hexadecimal
           value HH (one or two hex digits)
    \uHHHH the Unicode (ISO/IEC 10646) character whose value is 
           the hexadecimal value HHHH (one to four hex digits)
    \UHHHHHHHH the Unicode (ISO/IEC 10646) character whose value 
               is the hexadecimal value HHHHHHHH (one to eight 
               hex digits)
    \cx    a control-x character

ผลลัพธ์ที่ขยายออกมาเป็นแบบเสนอราคาเดียวราวกับว่าไม่มีเครื่องหมายดอลลาร์อยู่


ดูคำพูดและการหลบหนี: ANSI C ชอบสตริงใน bash-hackers.org wiki สำหรับรายละเอียดเพิ่มเติม นอกจากนี้โปรดทราบว่าไฟล์"การเปลี่ยนแปลงทุบตี" ( ภาพรวมที่นี่ ) กล่าวถึงการเปลี่ยนแปลงและแก้ไขข้อบกพร่องมากมายที่เกี่ยวข้องกับ$'string'กลไกการอ้างอิง

อ้างอิงจาก unix.stackexchange.com วิธีการใช้ตัวอักษรพิเศษเป็นตัวละครธรรมดา มันควรจะทำงานได้ (ในบางรูปแบบ) ใน bash, zsh, mksh, ksh93 และ FreeBSD และ busybox sh


สามารถใช้งานได้ แต่สตริงที่ยกมาเดี่ยวที่นี่ไม่ได้เป็นหนึ่งเดียวที่ยกมาจริงเนื้อหาในสตริงนี้อาจถูก interprested โดยเปลือก: echo $'foo\'b!ar'=> !ar': event not found
regilero

2
บนเครื่องของฉัน > echo $BASH_VERSION 4.2.47(1)-release > echo $'foo\'b!ar' foo'b!ar
mj41

1
ใช่นั่นคือเหตุผลสำหรับ "พฤษภาคม" ฉันมีไว้ใน Red hat 6.4 แน่นอนว่าเป็นเวอร์ชั่นทุบตีที่เก่ากว่า
regilero

Bash ChangeLogมีการแก้ไขข้อผิดพลาดมากมายที่เกี่ยวข้องกับ$'วิธีที่ง่ายที่สุดคือการลองด้วยตัวเองในระบบเก่า
mj41

จะตระหนักถึง: e. Bash no longer inhibits C-style escape processing ($'...') while performing pattern substitution word expansions.นำมาจากtiswww.case.edu/php/chet/bash/CHANGES ยังคงใช้งานได้ใน 4.3.42 แต่ไม่ใช่ใน 4.3.48
stiller_leser

49

ฉันไม่เห็นรายการในบล็อกของเขา (ลิงค์โปรด?) แต่ตามคู่มืออ้างอิง gnu :

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

ดังนั้นทุบตีจะไม่เข้าใจ:

alias x='y \'z '

อย่างไรก็ตามคุณสามารถทำได้ถ้าคุณล้อมรอบด้วยเครื่องหมายคำพูดคู่:

alias x="echo \'y "
> x
> 'y


เนื้อหาที่ถูกล้อมรอบด้วยเครื่องหมายคำพูดคู่กำลังได้รับการประเมินเพื่อให้มีเพียงเครื่องหมายคำพูดเดี่ยวในเครื่องหมายคำพูดคู่ตามที่ liori แนะนำว่าน่าจะเป็นคำตอบที่เหมาะสม
Piotr Dobrogost

3
นี่คือคำตอบที่แท้จริงสำหรับคำถาม ในขณะที่คำตอบที่ยอมรับอาจมีวิธีแก้ปัญหา แต่ในทางเทคนิคแล้วจะตอบคำถามที่ไม่ได้ถาม
Matthew G

3
แมทธิวคำถามนั้นเกี่ยวกับการหลีกเลี่ยงคำพูดเดียวในคำพูดเดียว คำตอบนี้ขอให้ผู้ใช้เปลี่ยนพฤติกรรมของพวกเขาและหากคุณมีอุปสรรคในการใช้เครื่องหมายคำพูดคู่ (ตามชื่อคำถามที่แนะนำ) คำตอบนี้จะไม่ช่วย มันค่อนข้างมีประโยชน์แม้ว่า (เห็นได้ชัดแม้ว่า) และเป็นเช่นนั้นควรได้รับการโหวต แต่คำตอบที่ได้รับการยอมรับจะกล่าวถึงปัญหาที่แม่นยำที่ Op ถามเกี่ยวกับ
Fernando Cordeiro

ไม่จำเป็นต้องอ้างคำพูดเดียวในสตริงคำพูดคู่
Matthew D. Scholefield

32

ฉันสามารถยืนยันได้ว่าการใช้'\''คำพูดเดียวภายในสตริงที่มีเครื่องหมายคำพูดเดี่ยวใช้งานได้ใน Bash และสามารถอธิบายได้ในลักษณะเดียวกับอาร์กิวเมนต์ "ติดกาว" จากก่อนหน้าในเธรด สมมติว่าเรามีสตริงที่ยกมา: 'A '\''B'\'' C'(คำพูดทั้งหมดที่นี่เป็นคำพูดเดียว) ถ้ามันถูกส่งผ่านไปยังเสียงสะท้อนมันจะพิมพ์สิ่งต่อไปนี้: A 'B' C. ใน'\''อัญประกาศแรกจะปิดสตริงที่\'มีเครื่องหมายคำพูดเดี่ยวปัจจุบันเครื่องหมายต่อไปนี้จะเป็นเครื่องหมายคำพูดเดียวกับสตริงก่อนหน้า ( \'เป็นวิธีการระบุเครื่องหมายคำพูดเดี่ยวโดยไม่ต้องเริ่มต้นสตริงที่อ้างถึง) และเครื่องหมายคำพูดสุดท้ายจะเปิดสตริงเดี่ยว


2
นี่คือการเข้าใจผิดไวยากรณ์นี้ '\' 'ไม่ไป "ภายใน" สตริงที่ยกมาเดียว ในคำแถลงนี้ 'A' \ '' B '\' 'C' คุณกำลังเชื่อมสตริง 5 \ escape และอัญประกาศเดี่ยว
teknopaul

1
@teknopaul การกำหนดalias something='A '\''B'\'' C'ส่งผลให้somethingเป็นสายอักขระเดียวดังนั้นแม้ในขณะที่ด้านขวามือของการมอบหมายไม่ใช่เทคนิคสายเดียวฉันไม่คิดว่ามันจะสำคัญมาก
Teemu Leisti

ขณะนี้ทำงานในตัวอย่างของคุณจะไม่ได้ในทางเทคนิคการให้บริการโซลูชั่นสำหรับวิธีการแทรกคำพูดเดียวภายในสายยกเดียว คุณได้อธิบายไปแล้ว แต่ใช่มันกำลังทำ'A ' + ' + 'B' + ' + ' C'อยู่ กล่าวอีกนัยหนึ่งโซลูชันสำหรับการแทรกอักขระเครื่องหมายคำพูดเดี่ยวภายในสตริงที่ยกมาเดี่ยวควรอนุญาตให้ฉันสร้างสตริงดังกล่าวด้วยตัวเองและพิมพ์ อย่างไรก็ตามวิธีนี้จะไม่ทำงานในกรณีนี้ STR='\''; echo $STR. ตามที่ออกแบบไว้ BASH ไม่อนุญาตสิ่งนี้อย่างแท้จริง
krb686

@mikhail_b ใช่'\''ใช้ได้สำหรับการทุบตี คุณช่วยชี้ให้เห็นว่าส่วนใดของgnu.org/software/bash/manual/bashref.htmlระบุพฤติกรรมดังกล่าว
Jingguo Yao

20

ทั้งสองเวอร์ชันกำลังทำงานไม่ว่าจะด้วยการต่อข้อมูลโดยใช้อักขระอัญประกาศเดี่ยว (\ ') หรือการต่อข้อมูลโดยการใส่อักขระเครื่องหมายคำพูดเดี่ยวไว้ภายในเครื่องหมายคำพูดคู่ ("'")

ผู้เขียนคำถามไม่ได้สังเกตว่ามีการเสนอราคาพิเศษ (') ในตอนท้ายของความพยายามหลบหนีครั้งสุดท้ายของเขา:

alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''
           │         │┊┊|       │┊┊│     │┊┊│       │┊┊│
           └─STRING──┘┊┊└─STRIN─┘┊┊└─STR─┘┊┊└─STRIN─┘┊┊│
                      ┊┊         ┊┊       ┊┊         ┊┊│
                      ┊┊         ┊┊       ┊┊         ┊┊│
                      └┴─────────┴┴───┰───┴┴─────────┴┘│
                          All escaped single quotes    │
                                                       │
                                                       ?

ดังที่คุณเห็นในชิ้นส่วนที่ดีของ ASCII / Unicode art ก่อนหน้าคำพูดเดี่ยวที่หลบหนีล่าสุด (\ ') จะตามด้วยเครื่องหมายคำพูดเดี่ยวที่ไม่จำเป็น (') การใช้ปากกาเน้นข้อความแบบเดียวกับที่มีอยู่ใน Notepad ++ สามารถพิสูจน์ได้ว่ามีประโยชน์มาก

เหมือนกันกับตัวอย่างอื่นเช่นตัวอย่างต่อไปนี้:

alias rc='sed '"'"':a;N;$!ba;s/\n/, /g'"'"
alias rc='sed '\'':a;N;$!ba;s/\n/, /g'\'

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

$ cat Little_Commas.TXT
201737194
201802699
201835214

$ rc Little_Commas.TXT
201737194, 201802699, 201835214

3
อัปเดตแล้วสำหรับภาพประกอบตาราง ASCII :)
php-dev

16

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

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

มันทำโดยการเปิดแล้วเสร็จหนึ่ง ( ') วางหนีหนึ่ง ( \') แล้วเปิดอีกหนึ่ง ( ') ไวยากรณ์นี้ใช้ได้กับทุกคำสั่ง มันคล้ายกันมากกับคำตอบที่ 1


15

ฉันไม่ได้กล่าวถึงประเด็นการอ้างอิงโดยเฉพาะเพราะบางครั้งมันก็สมเหตุสมผลที่จะพิจารณาแนวทางอื่น

rxvt() { urxvt -fg "#${1:-000000}" -bg "#${2:-FFFFFF}"; }

ซึ่งคุณสามารถโทรเป็น:

rxvt 123456 654321

แนวคิดที่ว่าคุณสามารถใช้ชื่อแทนได้โดยไม่ต้องกังวลเรื่องราคา:

alias rxvt='rxvt 123456 654321'

หรือหากคุณต้องการรวมการ#โทรเข้าทั้งหมดด้วยเหตุผลบางประการ:

rxvt() { urxvt -fg "${1:-#000000}" -bg "${2:-#FFFFFF}"; }

ซึ่งคุณสามารถโทรเป็น:

rxvt '#123456' '#654321'

แน่นอนว่านามแฝงคือ:

alias rxvt="rxvt '#123456' '#654321'"

(อุ๊ปส์ฉันเดาว่าฉันตอบคำถามได้ไหม :)


1
ฉันพยายามที่จะใส่อะไรบางอย่างในเครื่องหมายคำพูดเดี่ยวที่อยู่ในเครื่องหมายคำพูดคู่ซึ่งก็คือในคำพูดเดียว Yikes ขอบคุณสำหรับคำตอบของ "ลองใช้วิธีอื่น" นั่นทำให้ความแตกต่าง
Clinton Blackmore

1
ฉันมา 5 ปีแล้ว แต่คุณไม่ได้อ้างคำพูดเดียวในนามแฝงครั้งสุดท้ายของคุณใช่ไหม
Julien

1
@Julien ฉันไม่เห็นปัญหา ;-)
nicerobot

11

เนื่องจากไม่สามารถใส่เครื่องหมายคำพูดเดี่ยวภายในสตริงที่ยกมาเดี่ยวตัวเลือกที่ง่ายและอ่านง่ายที่สุดคือการใช้สตริง HEREDOC

command=$(cat <<'COMMAND'
urxvt -fg '#111111' -bg '#111111'
COMMAND
)

alias rxvt=$command

ในรหัสข้างต้น HEREDOC จะถูกส่งไปยังcatคำสั่งและผลลัพธ์ของสิ่งนั้นถูกกำหนดให้กับตัวแปรผ่านสัญกรณ์การทดแทนคำสั่ง$(..)

การใส่เครื่องหมายคำพูดเดี่ยวรอบ ๆ HEREDOC เป็นสิ่งจำเป็นเนื่องจากอยู่ภายใน $()


ฉันหวังว่าฉันจะเลื่อนลงก่อนหน้านี้ - ฉันสร้างนวัตกรรมใหม่ของวิธีการนี้และมาที่นี่เพื่อโพสต์มัน! นี่สะอาดกว่าและอ่านได้มากกว่าวิธีหลบหนีอื่น ๆ ทั้งหมด ไม่ใช่มันจะไม่ทำงานบนเชลล์ที่ไม่ใช่ bash บางตัวเช่นdashซึ่งเป็นเชลล์เริ่มต้นในสคริปต์ upstart ของ Ubuntu และที่อื่น ๆ
Korny

ขอบคุณ! สิ่งที่ฉันมองหาวิธีกำหนดคำสั่งตามที่อยู่คือผ่าน heredoc และส่งคำสั่ง auto escaped ไปยัง ssh BTW cat << คำสั่งที่ไม่มีเครื่องหมายคำพูดอนุญาตให้แก้ไข vatiables ภายในคำสั่งและทำงานได้ดีสำหรับวิธีการนี้
Igor Tverdovskiy

10

ฉันแค่ใช้รหัสเชลล์ .. เช่น\x27หรือ\\x22บังคับ ไม่ยุ่งยากเลยทีเดียว


คุณสามารถแสดงตัวอย่างของการดำเนินการนี้ได้หรือไม่? สำหรับฉันมันแค่พิมพ์ตัวอักษรx27(บน Centos 6.6)
Will Sheppard

6

คำตอบเหล่านี้ส่วนใหญ่เกิดขึ้นกับกรณีเฉพาะที่คุณถาม มีวิธีการทั่วไปที่เพื่อนและผมได้พัฒนาที่ช่วยให้การโดยพลการอ้างในกรณีที่คุณจำเป็นต้องอ้างทุบตีคำสั่งผ่านหลายชั้นของการขยายตัวของเปลือกเช่นผ่าน SSH, su -c, bash -cฯลฯ มีเป็นหนึ่งในหลักดั้งเดิมที่คุณต้องการที่นี่ ในทุบตีพื้นเมือง:

quote_args() {
    local sq="'"
    local dq='"'
    local space=""
    local arg
    for arg; do
        echo -n "$space'${arg//$sq/$sq$dq$sq$dq$sq}'"
        space=" "
    done
}

สิ่งนี้ทำในสิ่งที่มันบอก: มัน shell-อัญประกาศแต่ละอาร์กิวเมนต์เป็นรายบุคคล (หลังจากการขยาย bash, แน่นอน):

$ quote_args foo bar
'foo' 'bar'
$ quote_args arg1 'arg2 arg2a' arg3
'arg1' 'arg2 arg2a' 'arg3'
$ quote_args dq'"'
'dq"'
$ quote_args dq'"' sq"'"
'dq"' 'sq'"'"''
$ quote_args "*"
'*'
$ quote_args /b*
'/bin' '/boot'

มันเป็นสิ่งที่ชัดเจนสำหรับการขยายชั้นหนึ่ง:

$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2

(โปรดทราบว่า$(quote_args ...)จำเป็นต้องใช้เครื่องหมายอัญประกาศคู่ล้อมรอบเพื่อสร้างผลลัพธ์ให้เป็นอาร์กิวเมนต์เดียวจนถึงbash -c) และสามารถนำมาใช้เพื่ออ้างอิงอย่างถูกต้องผ่านการขยายหลายชั้น:

$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2

ตัวอย่างข้างต้น:

  1. shell-quote แต่ละอาร์กิวเมนต์ไปยังด้านในquote_argsทีละรายการจากนั้นรวมผลลัพธ์ที่ได้ลงในอาร์กิวเมนต์เดียวกับเครื่องหมายคำพูดคู่ด้านใน
  2. เปลือกคำพูดbash, -cและผลแล้วครั้งหนึ่งที่ยกมาจากขั้นตอนที่ 1 แล้วรวมผลเป็นอาร์กิวเมนต์เดียวกับคำพูดสองด้านนอก
  3. bash -cส่งระเบียบที่เป็นอาร์กิวเมนต์ไปด้านนอก

นั่นเป็นความคิดที่สั้น คุณสามารถทำสิ่งที่ค่อนข้างซับซ้อนได้ด้วยสิ่งนี้ แต่คุณต้องระวังเกี่ยวกับลำดับของการประเมินและเกี่ยวกับสารตั้งต้นที่ยกมา ตัวอย่างเช่นต่อไปนี้ทำสิ่งที่ผิด (สำหรับคำนิยามของ "ผิด" บางอย่าง):

$ (cd /tmp; bash -c "$(quote_args cd /; pwd 1>&2)")
/tmp
$ (cd /tmp; bash -c "$(quote_args cd /; [ -e *sbin ] && echo success 1>&2 || echo failure 1>&2)")
failure

ในตัวอย่างแรกทุบตีทันทีขยายquote_args cd /; pwd 1>&2เป็นสองคำสั่งที่แยกจากกันquote_args cd /และpwd 1>&2ดังนั้น CWD ยังคงเป็น/tmpเมื่อpwdคำสั่งจะถูกดำเนินการ ตัวอย่างที่สองแสดงให้เห็นถึงปัญหาที่คล้ายกันสำหรับ globbing แท้จริงแล้วปัญหาพื้นฐานเดียวกันนี้เกิดขึ้นกับการขยาย bash ทั้งหมด ปัญหาที่นี่คือการทดแทนคำสั่งไม่ใช่การเรียกใช้ฟังก์ชัน: เป็นการประเมินสคริปต์ bash หนึ่งตัวและใช้เอาต์พุตเป็นส่วนหนึ่งของสคริปต์ bash อื่น

หากคุณพยายามที่จะหลบหนีตัวดำเนินการเชลล์คุณจะล้มเหลวเพราะสตริงผลลัพธ์ที่ส่งผ่านไปbash -cเป็นเพียงลำดับของสตริงที่ยกมาเป็นรายบุคคลซึ่งจะไม่ถูกตีความว่าเป็นตัวดำเนินการซึ่งง่ายต่อการดูว่าคุณสะท้อนสตริงที่จะ ถูกส่งไปทุบตี:

$ (cd /tmp; echo "$(quote_args cd /\; pwd 1\>\&2)")
'cd' '/;' 'pwd' '1>&2'
$ (cd /tmp; echo "$(quote_args cd /\; \[ -e \*sbin \] \&\& echo success 1\>\&2 \|\| echo failure 1\>\&2)")
'cd' '/;' '[' '-e' '*sbin' ']' '&&' 'echo' 'success' '1>&2' '||' 'echo' 'failure' '1>&2'

ปัญหาที่นี่คือคุณกำลังอ้างถึงมากเกินไป สิ่งที่คุณต้องการสำหรับโอเปอเรเตอร์ที่จะไม่ต้องใส่เครื่องหมายคำพูดเป็นอินพุตของการปิดล้อมbash -cซึ่งหมายความว่าพวกเขาจำเป็นต้องอยู่นอกการ$(quote_args ...)ทดแทนคำสั่ง

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

$ (cd /tmp; echo "$(quote_args cd /); $(quote_args pwd) 1>&2")
'cd' '/'; 'pwd' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")
/
$ (cd /tmp; echo "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
'cd' '/'; [ -e *'sbin' ] && 'echo' 'success' 1>&2 || 'echo' 'failure' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
success

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

$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")"
/
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")"
/
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")")"
/
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")"
success
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *sbin ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")"
success
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")")"
success

เป็นต้น

ตัวอย่างเหล่านี้อาจดูเหมือนให้ประณีตว่าคำพูดชอบsuccess, sbinและpwdไม่จำเป็นต้องเป็นเปลือกที่ยกมา แต่จุดสำคัญที่ต้องจำไว้เมื่อเขียนสคริปต์การป้อนข้อมูลโดยพลการคือการที่คุณต้องการที่จะพูดทุกอย่างที่คุณไม่แน่ใจว่าอย่างdoesn' ทีRobert'; rm -rf /จำเป็นต้องอ้างเพราะคุณไม่เคยรู้ว่าเมื่อผู้ใช้จะโยนใน

เพื่อให้เข้าใจสิ่งที่เกิดขึ้นภายใต้หน้ากากได้ดีขึ้นคุณสามารถเล่นกับฟังก์ชั่นตัวช่วยขนาดเล็กสองแบบ:

debug_args() {
    for (( I=1; $I <= $#; I++ )); do
        echo -n "$I:<${!I}> " 1>&2
    done
    echo 1>&2
}

debug_args_and_run() {
    debug_args "$@"
    "$@"
}

ที่จะแจกแจงแต่ละอาร์กิวเมนต์ให้กับคำสั่งก่อนที่จะดำเนินการ:

$ debug_args_and_run echo a'"'b"'"c arg2
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)"
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'bash'"'"' '"'"'-c'"'"' '"'"''"'"'"'"'"'"'"'"'debug_args_and_run'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'echo'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'a"b'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'c'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'arg2'"'"'"'"'"'"'"'"''"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

สวัสดีไคล์ vagrant ssh -c {single-arg} guestวิธีการแก้ปัญหาของคุณทำงานที่ดีสำหรับกรณีที่ฉันได้เมื่อฉันต้องการที่จะผ่านกลุ่มของการขัดแย้งเป็นอาร์กิวเมนต์เดียว: {single-arg}ความต้องการที่จะได้รับการปฏิบัติเป็นหาเรื่องเดียวเพราะคนจรจัดใช้เวลาหาเรื่องต่อไปหลังจากที่มันเป็นชื่อของผู้เข้าพัก ไม่สามารถเปลี่ยนแปลงคำสั่งซื้อได้ {single-arg}แต่ผมต้องผ่านคำสั่งและการขัดแย้งภายใน ดังนั้นผมจึงใช้ของคุณquote_args()จะพูดคำสั่งและ args vagrant ssh -c "'command' 'arg 1 with blanks' 'arg 2'" guestของตนและใส่คำพูดสองรอบผลและการทำงานเช่นเสน่ห์: ขอบคุณ !!!
Andreas Maier

6

IMHO คำตอบที่แท้จริงคือคุณไม่สามารถหลีกเลี่ยงอัญประกาศเดี่ยวภายในสตริงที่ยกมาเดี่ยว

มันเป็นไปไม่ได้.

ถ้าเราเข้าใจว่าเรากำลังใช้ bash

จากคู่มือทุบตี ...

Enclosing characters in single quotes preserves the literal value of each
character within the quotes.  A single quote may not occur
between single quotes, even when preceded by a backslash.

คุณต้องใช้หนึ่งในกลไกการหลบหนีสตริงอื่น "หรือ \

ไม่มีอะไรที่วิเศษเกี่ยวกับaliasความต้องการที่จะใช้คำพูดเดียว

ทั้งงานต่อไปนี้ในทุบตี

alias rxvt="urxvt -fg '#111111' -bg '#111111'"
alias rxvt=urxvt\ -fg\ \'#111111\'\ -bg\ \'#111111\'

ตัวหลังใช้ \ เพื่อหนีอักขระช่องว่าง

นอกจากนี้ยังไม่มีอะไรมหัศจรรย์เกี่ยวกับ # 111111 ที่ต้องใช้เครื่องหมายคำพูดเดี่ยว

ตัวเลือกต่อไปนี้ได้ผลลัพธ์เดียวกันทั้งสองตัวเลือกซึ่งในนามแฝง rxvt ทำงานตามที่คาดไว้

alias rxvt='urxvt -fg "#111111" -bg "#111111"'
alias rxvt="urxvt -fg \"#111111\" -bg \"#111111\""

คุณสามารถหลีกเลี่ยงปัญหา # โดยตรงได้

alias rxvt="urxvt -fg \#111111 -bg \#111111"

"คำตอบที่แท้จริงคือคุณไม่สามารถหลีกเลี่ยงอัญประกาศเดี่ยวภายในสตริงที่ยกมาเดี่ยว" นั่นเป็นความจริงทางเทคนิค แต่คุณสามารถมีวิธีแก้ปัญหาที่เริ่มต้นด้วยคำพูดเดียวจบลงด้วยคำพูดเดียวและมีเพียงคำพูดเดียวที่อยู่ตรงกลาง stackoverflow.com/a/49063038
รอบรู้

ไม่ได้โดยการหลบหนี แต่โดยการต่อข้อมูลเท่านั้น
teknopaul

4

ในตัวอย่างที่กำหนดให้ใช้เพียงเครื่องหมายคำพูดคู่แทนคำพูดเดี่ยวเป็นกลไกการหลบหนีภายนอก:

alias rxvt="urxvt -fg '#111111' -bg '#111111'"

วิธีการนี้เหมาะสำหรับหลาย ๆ กรณีที่คุณต้องการส่งผ่านสตริงคงที่ไปยังคำสั่ง: เพียงตรวจสอบว่าเชลล์จะตีความสตริงที่echoมีเครื่องหมายคำพูดสองครั้งผ่านและและยกเว้นอักขระด้วยเครื่องหมายแบ็กสแลชหากจำเป็น

ในตัวอย่างคุณจะเห็นว่าเครื่องหมายคำพูดคู่นั้นเพียงพอที่จะป้องกันสตริง:

$ echo "urxvt -fg '#111111' -bg '#111111'"
urxvt -fg '#111111' -bg '#111111'

4

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

$ rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'
$ echo $rxvt
urxvt -fg '#111111' -bg '#111111'

คำอธิบาย

กุญแจสำคัญคือคุณสามารถปิดใบเสนอราคาเดียวและเปิดใหม่ได้หลายครั้งตามที่คุณต้องการ ยกตัวอย่างเช่นเป็นเช่นเดียวกับfoo='a''b' foo='ab'ดังนั้นคุณสามารถปิดคำพูดเดียวโยนคำพูดตัวอักษรเดียว\'แล้วเปิดคำพูดเดี่ยวต่อไปอีกครั้ง

แผนภาพแสดงรายละเอียด

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

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'    # original
[^^^^^^^^^^] ^[^^^^^^^] ^[^^^^^] ^[^^^^^^^] ^    # show open/close quotes
 urxvt -fg   ' #111111  '  -bg   ' #111111  '    # literal characters remaining

(นี่เป็นคำตอบเดียวกับเอเดรียน แต่ฉันรู้สึกว่านี่จะอธิบายได้ดีกว่านอกจากนี้คำตอบของเขายังมีคำพูดเดียวที่ไม่จำเป็น 2 อันในตอนท้าย)


+1 สำหรับการใช้'\''วิธีที่ฉันแนะนำเหนือ'"'"'วิธีซึ่งมักจะยากสำหรับมนุษย์ที่จะอ่าน
mtraceur

3

นี่คือรายละเอียดเกี่ยวกับ The One True Answer ที่อ้างถึงข้างต้น:

บางครั้งฉันจะดาวน์โหลดโดยใช้ rsync บน ssh และต้องหลีกเลี่ยงชื่อไฟล์ด้วย 'สองครั้ง! (OMG!) หนึ่งครั้งสำหรับทุบตีและอีกครั้งสำหรับ ssh หลักการเดียวกันของตัวคั่นใบเสนอราคาแบบสลับอยู่ที่ทำงานที่นี่

ตัวอย่างเช่นสมมติว่าเราต้องการได้: LA Stories ของ Louis Theroux ...

  1. ขั้นแรกให้คุณใส่ Louis Theroux ไว้ในเครื่องหมายคำพูดเดี่ยวสำหรับ bash และ double quotes สำหรับ ssh: '"Louis Theroux"'
  2. จากนั้นคุณใช้เครื่องหมายคำพูดเดี่ยวเพื่อหนีเครื่องหมายคำพูดคู่ '' '
  3. การใช้เครื่องหมายคำพูดคู่เพื่อหนีเครื่องหมายอัญประกาศ "" "
  4. จากนั้นทำซ้ำ # 2 โดยใช้เครื่องหมายคำพูดเดี่ยวเพื่อหลีกเลี่ยงเครื่องหมายคำพูดคู่ '' '
  5. จากนั้นใส่เรื่องราว LA ในการเสนอราคาเดี่ยวสำหรับการทุบตีและการเสนอราคาซ้ำสำหรับ ssh: '"LA Stories"'

และดูเถิด! คุณจบลงด้วยสิ่งนี้:

rsync -ave ssh '"Louis Theroux"''"'"'"'"''"s LA Stories"'

ซึ่งเป็นงานอันยิ่งใหญ่สำหรับหนึ่งน้อย - แต่คุณไป


3
shell_escape () {
    printf '%s' "'${1//\'/\'\\\'\'}'"
}

คำอธิบายการใช้งาน:

  • เครื่องหมายอัญประกาศคู่เพื่อให้เราสามารถแสดงผลการตัดคำพูดคำเดียวและใช้${...}ไวยากรณ์ได้อย่างง่ายดาย

  • การค้นหาและแทนที่ของ bash มีลักษณะดังนี้: ${varname//search/replacement}

  • เรากำลังแทนที่'ด้วย'\''

  • '\''เข้ารหัสเดี่ยว'เช่นนั้น:

    1. ' จบการอ้างอิงเดียว

    2. \'เข้ารหัส a '(จำเป็นต้องใช้แบ็กสแลชเนื่องจากเราไม่ได้อยู่ในเครื่องหมายคำพูด)

    3. ' เริ่มต้นการอ้างคำเดียวอีกครั้ง

    4. ทุบตีโดยอัตโนมัติเชื่อมสตริงที่ไม่มีช่องว่างระหว่าง

  • มี\ทุกครั้งก่อน\และเนื่องจากว่าเป็นกฎระเบียบสำหรับการหลบหนี'${...//.../...}

string="That's "'#@$*&^`(@#'
echo "original: $string"
echo "encoded:  $(shell_escape "$string")"
echo "expanded: $(bash -c "echo $(shell_escape "$string")")"

PS เข้ารหัสเป็นสตริงที่ยกมาเดี่ยวเสมอเพราะมันง่ายกว่าสตริงที่ยกมาสองเท่า


2

อีกวิธีหนึ่งในการแก้ไขปัญหาของเลเยอร์ใบเสนอราคาซ้อนกันมากเกินไป:

คุณกำลังพยายามยัดเข้าไปในพื้นที่เล็กเกินไปจนเกินไปดังนั้นให้ใช้ฟังก์ชั่นทุบตี

ปัญหาคือคุณกำลังพยายามทำรังในระดับมากเกินไปและเทคโนโลยีนามแฝงพื้นฐานไม่ทรงพลังพอที่จะรองรับ ใช้ฟังก์ชั่นทุบตีเช่นนี้เพื่อให้เครื่องหมายคำพูดคู่ซ้ำกันและส่งผ่านพารามิเตอร์ถูกจัดการตามปกติตามที่เราคาดหวัง:

lets_do_some_stuff() {
    tmp=$1                       #keep a passed in parameter.
    run_your_program $@          #use all your passed parameters.
    echo -e '\n-------------'    #use your single quotes.
    echo `date`                  #use your back ticks.
    echo -e "\n-------------"    #use your double quotes.
}
alias foobarbaz=lets_do_some_stuff

จากนั้นคุณสามารถใช้ตัวแปร $ 1 และ $ 2 และเครื่องหมายคำพูดคู่และเครื่องหมายขีดคู่ด้านหลังโดยไม่ต้องกังวลเกี่ยวกับฟังก์ชันนามแฝงที่ทำลายความสมบูรณ์ของพวกเขา

โปรแกรมนี้พิมพ์:

el@defiant ~/code $ foobarbaz alien Dyson ring detected @grid 10385
alien Dyson ring detected @grid 10385
-------------
Mon Oct 26 20:30:14 EDT 2015
-------------

2

หากคุณมี GNU Parallel ติดตั้งอยู่คุณสามารถใช้การอ้างอิงภายใน:

$ parallel --shellquote
L's 12" record
<Ctrl-D>
'L'"'"'s 12" record'
$ echo 'L'"'"'s 12" record'
L's 12" record

จากเวอร์ชั่น 20190222 คุณสามารถทำได้--shellquoteหลายครั้ง:

$ parallel --shellquote --shellquote --shellquote
L's 12" record
<Ctrl-D>
'"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
$ eval eval echo '"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
L's 12" record

มันจะอ้างอิงสตริงในเชลล์ที่รองรับทั้งหมด (ไม่เพียง แต่bash)


1

ฟังก์ชั่นนี้:

quote () 
{ 
    local quoted=${1//\'/\'\\\'\'};
    printf "'%s'" "$quoted"
}

ช่วยให้ข้อความของภายใน' 'ใช้เป็นสิ่งนี้:

$ quote "urxvt -fg '#111111' -bg '#111111'"
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

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

#!/bin/bash

quote ()
{
    local quoted=${1//\'/\'\\\'\'};
    printf "'%s'" "$quoted"
}

while read line; do
    quote "$line"
done <<-\_lines_to_quote_
urxvt -fg '#111111' -bg '#111111'
Louis Theroux's LA Stories
'single quote phrase' "double quote phrase"
_lines_to_quote_

จะส่งออก:

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
'Louis Theroux'\''s LA Stories'
''\''single quote phrase'\'' "double quote phrase"'

สตริงทั้งหมดที่ยกมาอย่างถูกต้องภายในคำพูดเดียว


1

หากคุณกำลังสร้างสตริงเชลล์ภายใน Python 2 หรือ Python 3 ต่อไปนี้อาจช่วยอ้างอาร์กิวเมนต์:

#!/usr/bin/env python

from __future__ import print_function

try:  # py3
    from shlex import quote as shlex_quote
except ImportError:  # py2
    from pipes import quote as shlex_quote

s = """foo ain't "bad" so there!"""

print(s)
print(" ".join([shlex_quote(t) for t in s.split()]))

สิ่งนี้จะออก:

foo ain't "bad" so there!
foo 'ain'"'"'t' '"bad"' so 'there!'

1

นี่คือสองเซ็นต์ของฉัน - ในกรณีถ้าใครอยากเป็นแบบshพกพาไม่ใช่bashเฉพาะ -specific (แก้ปัญหาไม่ได้มีประสิทธิภาพมากเกินไปแม้ว่ามันจะเริ่มโปรแกรมภายนอก - sed):

  • ใส่สิ่งนี้ในquote.sh(หรือเพียงquote) ที่ใดที่หนึ่งบนPATH:
# ใช้งานได้กับอินพุตมาตรฐาน (stdin)
อ้าง () {
  echo -n "'";
  sed 's / \ ([' "'"'] ['"' ''] * \) / '' '' '' \ \ 1" '"' '' / g ';
  echo -n "'"
}

กรณี "$ 1" ใน
 -) อ้าง;
 *) echo "การใช้งาน: cat ... | quote - # อินพุตคำพูดเดี่ยวสำหรับเชลล์เป้าหมาย" 2> & 1 ;;
esac

ตัวอย่าง:

$ echo -n "G'day, mate!" | ./quote.sh -
'G' "'"' วันเพื่อน! '

และแน่นอนว่ามันแปลงกลับ:

$ echo 'G' "'' 'วันเพื่อน!'
เพื่อนวัน!

คำอธิบาย:โดยทั่วไปเราจะต้องใส่ข้อมูลด้วยเครื่องหมายคำพูด'แล้วแทนที่คำพูดใด ๆ ที่อยู่ในไมโครมอนสเตอร์ตัวนี้: '"'"'(จบคำพูดเปิดด้วยการจับคู่'หนีคำพูดเดียวที่พบโดยห่อด้วยเครื่องหมายคำพูดคู่ - "'"และ แล้วในที่สุดก็ออกเปิดใหม่คำพูดเดียว'หรือหลอกสัญกรณ์: ' + "'" + ' == '"'"')

วิธีมาตรฐานวิธีหนึ่งในการทำเช่นนั้นคือใช้sedกับคำสั่งการแทนที่ต่อไปนี้:

s/\(['][']*\)/'"\1"'/g 

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

sed 's/\(['"'"']['"'"']*\)/'"'"'"\1"'"'"'/g' 

(และวิธีที่ดีวิธีหนึ่งในการสร้างผลลัพธ์นี้คือให้อาหารต้นฉบับของs/\(['][']*\)/'"\1"'/gKyle Rose หรือสคริปต์ของ George V. Reilly)

ในที่สุดมันก็สมเหตุสมผลที่จะคาดหวังว่าอินพุตจะมาจากstdin- เนื่องจากการส่งผ่านอาร์กิวเมนต์บรรทัดคำสั่งอาจเป็นปัญหามากเกินไป

(โอ้และอาจเป็นเพราะเราต้องการเพิ่มข้อความช่วยเหลือเล็ก ๆ น้อย ๆ เพื่อให้สคริปต์ไม่แฮงค์เมื่อมีคนเรียกใช้งานเพราะ./quote.sh --helpสงสัยว่ามันทำอะไร)


0

นี่คือทางออกอื่น ฟังก์ชั่นนี้จะรับอาร์กิวเมนต์เดี่ยวและอ้างอย่างเหมาะสมโดยใช้อักขระเครื่องหมายคำพูดเดียวเช่นเดียวกับคำตอบที่โหวตด้านบนอธิบาย:

single_quote() {
  local quoted="'"
  local i=0
  while [ $i -lt ${#1} ]; do
    local ch="${1:i:1}"
    if [[ "$ch" != "'" ]]; then
      quoted="$quoted$ch"
    else
      local single_quotes="'"
      local j=1
      while [ $j -lt ${#1} ] && [[ "${1:i+j:1}" == "'" ]]; do
        single_quotes="$single_quotes'"
        ((j++))
      done
      quoted="$quoted'\"$single_quotes\"'"
      ((i+=j-1))
    fi
    ((i++))
  done
  echo "$quoted'"
}

ดังนั้นคุณสามารถใช้วิธีนี้:

single_quote "1 2 '3'"
'1 2 '"'"'3'"'"''

x="this text is quoted: 'hello'"
eval "echo $(single_quote "$x")"
this text is quoted: 'hello'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.