เหตุใด echo จึงเพิกเฉยอักขระคำพูดของฉัน


24

echoคำสั่งไม่ได้รวมถึงข้อความที่สมบูรณ์แบบที่ฉันให้มัน ตัวอย่างเช่นถ้าฉัน:

   $ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '

มันเอาท์พุท:

echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print } `

เครื่องหมายคำพูดเดี่ยว ( ') ที่ฉันมีในคำสั่ง echo ไม่รวมอยู่ด้วย หากฉันเปลี่ยนเป็นเครื่องหมายคำพูดคู่:

$ echo " echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `  "

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


3
ไม่แน่ใจว่าคุณกำลังพยายามทำอะไรที่นี่ ทำไมคุณทำคำสั่ง echo หนึ่งข้อความในอีกรายการหนึ่ง? หรือคุณพยายามพิมพ์คำสั่ง (ตัวอย่างหรือเช่นนั้น) อย่างแท้จริง?
Andy Smith

ฉันต้องการแทรกเอาต์พุต echo ไปยังไฟล์อื่น

คุณต้องอธิบายสิ่งที่คุณพยายามจะทำ เช่น "ฉันต้องการต่อท้ายข้อความต่อไปนี้: '... ' ไปยังไฟล์"
MikeyB

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

1
@Michael - ว้าว - ถ้าฉันสามารถให้ตัวแทนคุณสำหรับการแก้ไขของคุณฉันจะ - งานดีทำให้คำถามนี้ยอดเยี่ยมจริงๆ
Randall

คำตอบ:


33

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

$ echo "Hello world"
Hello world

ดังนั้นในตัวอย่างแรกของคุณหากคุณต้องการรวมเครื่องหมายคำพูดตามตัวอักษรในผลลัพธ์ของคุณพวกเขาจะต้องหลบหนี:

$ echo \'Hello world\'
'Hello world'

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

$ echo "'Hello world'"
'Hello world'

$ echo '"Hello world"'
"Hello world"

ในตัวอย่างที่สองของคุณคุณกำลังรันการทดแทนคำสั่งตรงกลางสตริง:

grep  $ARG  /var/tmp/setfile  | awk {print $2}

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

grep /var/tmp/setfile | awk {print}

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

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

echo \'' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '\'

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

echo "' echo PARAM=\`  grep  \$ARG  /var/tmp/setfile  | awk '{print \$2}' \`    '"

7

ฉันจะไม่พูดถึงรายละเอียดมากนักว่าทำไมความพยายามของคุณถึงวิธีที่พวกเขาทำเพราะคำตอบของ Michael Mrozekครอบคลุมเรื่องนี้ สรุปทุกอย่างระหว่างราคาเดียว ( '…') จะตีความตามตัวอักษร (และโดยเฉพาะอย่างยิ่งเป็นครั้งแรกใน'เครื่องหมายจุดสิ้นสุดของสตริงตัวอักษร) ในขณะที่`และรักษาความหมายพิเศษของพวกเขาระหว่าง$"…"

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

echo 'foo'\''bar'

พิมพ์นี้foo'barเพราะข้อโต้แย้งที่จะechoทำจากสตริงสามตัวเดียวที่ยกมาfoo, ตัดแบ่งกับตัวเดียว'(ที่ได้รับโดยการปกป้องตัวละคร'จากความหมายพิเศษผ่านก่อน\) barตัดแบ่งกับสตริงสามตัวเดียวที่ยกมา ดังนั้นแม้ว่าจะไม่ใช่สิ่งที่เกิดขึ้นเบื้องหลัง แต่คุณสามารถคิดว่า'\''เป็นวิธีการรวมการอ้างอิงเดียวในสายอักขระที่ยกมาเดี่ยว

หากคุณต้องการที่จะพิมพ์สตริงหลายซับซ้อนซึ่งเป็นเครื่องมือที่ดีกว่าคือที่นี่เอกสาร เอกสารที่นี่ประกอบด้วยอักขระสองตัว<<ตามด้วยเครื่องหมายเช่น<<EOFจากนั้นบางบรรทัดของข้อความจากนั้นเครื่องหมายสิ้นสุดเพียงอย่างเดียวในบรรทัด หากเครื่องหมายถูกยกมาในทางใดทางหนึ่ง ( 'EOF'หรือ"EOF"หรือ\EOFหรือ 'E "" ของ' หรือ ... ) จากนั้นข้อความจะถูกตีความอย่างแท้จริง (เช่นภายในเครื่องหมายคำพูดเดียวยกเว้นว่าแม้'จะเป็นตัวอักษรธรรมดา) หากเครื่องหมายไม่ได้ยกมาทั้งหมดแล้วข้อความที่ถูกตีความเช่นในสตริงดับเบิลอ้างถึงด้วย$\`การรักษาสถานะพิเศษของพวกเขา (แต่"และขึ้นบรรทัดใหม่จะถูกตีความอย่างแท้จริง)

cat <<'EOF'
echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `
EOF

1

โอเคสิ่งนี้จะใช้ได้: -

echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '

ทดสอบที่นี่: -

[asmith@yagi ~]$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '
 echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' ` 

0

ข้อเสนอแนะของ Gilles เกี่ยวกับการใช้เอกสารที่นี่เป็นสิ่งที่ดีจริงๆและแม้แต่ทำงานในภาษาสคริปต์เช่น Perl เป็นตัวอย่างที่เฉพาะเจาะจงตามคำตอบของเขาเกี่ยวกับปัญหาของ OP

use strict;
my $cmd = q#cat <<'EOF' # . "\n" .
          q#echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `# . "\n" .
          q#EOF# ;
open (my $OUTPUT, "$cmd |");
print $_ while <$OUTPUT>;
close $OUTPUT;

ได้รับตัวอย่างนี้มีการวางแผนเล็กน้อย แต่ฉันพบว่ามันเป็นเทคนิคที่มีประโยชน์สำหรับพูดส่งคำสั่ง SQL ไปที่psql(แทนcat)

(โปรดทราบว่าอักขระที่ไม่ใช่ตัวอักษรและช่องว่างที่ไม่ใช่ตัวอักษรใด ๆ สามารถใช้แทนการ#สร้างข้อความทั่วไปได้ด้านบน แต่แฮชดูเหมือนจะโดดเด่นสำหรับตัวอย่างนี้และไม่ถือว่าเป็นความคิดเห็น)


-1

ลองทำตามคำสั่งของคุณ

$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '

และแบ่งออกเป็นคำแรกโดยใช้ช่องว่างที่ไม่ได้อ้างอิงเป็นตัวคั่น:

Arg[1]=“' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print”
Arg[2]=“$2}' `    '”

ต่อไปเราจะทำการขยายตัวพารามิเตอร์: ขยายแต่ไม่ได้อยู่ใน$… '…'เนื่องจาก$2ว่างเปล่า (เว้นแต่คุณจะตั้งค่าผ่านเช่นset -- …) จะถูกแทนที่ด้วยสตริงว่าง

Arg[1]=“' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print”
Arg[2]=“}' `    '”

ถัดไปทำการลบเครื่องหมายคำพูด

Arg[1]=“ echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print”
Arg[2]=“} `    ”

อาร์กิวเมนต์เหล่านี้จะถูกส่งผ่านไปยัง echo ซึ่งจะพิมพ์ออกมาทีหลังคั่นด้วยช่องว่างหนึ่งช่อง

“ echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print } `    ”

ฉันหวังว่าคำสั่งทีละขั้นตอนนี้จะทำให้พฤติกรรมที่สังเกตเห็นชัดเจนขึ้น หากต้องการหลีกเลี่ยงปัญหาให้หลีกเลี่ยงคำพูดเดียวเช่นนี้:

$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '

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

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