Shell Script - ข้อผิดพลาดทางไวยากรณ์ใกล้โทเค็น `else 'ที่ไม่คาดคิด


15

ด้วยเชลล์สคริปต์ต่อไปนี้ทำไมฉันถึงได้รับข้อผิดพลาด

syntax error near unexpected token `else'

เชลล์สคริปต์

echo "please enter username"
read user_name
echo "please enter password"
read -s pass
echo ${ORACLE_SID}
SID=${ORACLE_SID}
if ["${ORACLE_SID}" != 'Test'] then
sqlplus -s -l $USER_NAME/$PASS@$SID <<EOF
copy from scott/tiger@orcl insert EMP using select * from EMP
exit
EOF
else
echo "Cannot copy"
fi


คุณอาจต้องการแก้ไขบรรทัด "คัดลอกจาก .... " เนื่องจากอาจแสดงสิ่งที่คุณไม่ต้องการแสดง (อย่างไรก็ตามฉันหวังว่าข้อมูลเหล่านั้นจะได้รับการแก้ไขแล้วเนื่องจากพวกเขาจะได้รับการรักษาความปลอดภัยที่แย่จริง ๆ )
Olivier Dulac

1
@OlivierDulac หากคุณอ้างถึงชื่อผู้ใช้และรหัสผ่านในบรรทัดนั้นผู้ใช้ฐานข้อมูล Oracle ทุกคนจะรู้จัก เป็นเรื่องปกติและเป็นที่รู้จักกันดีตั้งแต่จุดเริ่มต้นของฐานข้อมูล Oracle
Jåcob

@OlivierDulac ยินดีต้อนรับคุณบางข้อมูลเกี่ยวกับdba-oracle.com/t_scott_tiger.htm
Jåcob

คำตอบ:


25

คุณต้องยกเลิกเงื่อนไขifเช่นนี้:

if [ "${ORACLE_SID}" != 'Test' ]; then

หรือเช่นนี้

if [ "${ORACLE_SID}" != 'Test' ]
then

หมายเหตุ: คุณยังมีการใส่ช่องว่างหลังและก่อนที่[]

เหตุผลสำหรับการ;หรือ linebreak คือส่วนเงื่อนไขของifคำสั่งเป็นเพียงคำสั่ง คำสั่งใด ๆ ของความยาวใด ๆ ที่จะแม่นยำ เชลล์ดำเนินการคำสั่งนั้นตรวจสอบสถานะการออกของคำสั่งแล้วตัดสินใจว่าจะเรียกใช้งานthenส่วนหรือelseส่วนใดส่วนหนึ่ง

เนื่องจากคำสั่งสามารถมีความยาวเท่าใดก็ได้จึงจำเป็นต้องมีเครื่องหมายเพื่อทำเครื่องหมายจุดสิ้นสุดของส่วนเงื่อนไข นั่นคือหรือขึ้นบรรทัดใหม่ตามด้วย;then

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

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


นั่นคือจุดบน แต่ตอนนี้ฉันได้รับtest.sh: line 6: [: missing ] '`
Jåcob

@lesmana วิธีที่ดีในการให้คำตอบที่ผิดก่อนจากนั้นจึงทำการแก้ไขต่อไปก่อนที่คนอื่นจะให้คำตอบที่ถูกต้อง โปรดลองตอบคำถามให้ถูกต้องในครั้งแรก
Valentin Bajrami

1
ฉันไม่คิดว่าคำตอบแรกของฉันผิด ในความเป็นจริงมันเป็น "จุดที่" มันไม่ได้แก้ปัญหาทั้งหมดในคำถาม
เลสมานา

if เป็นไวยากรณ์ไม่ใช่คำสั่งทั่วไป มันเป็นคำที่สงวนไว้ แตกต่างจากภาษาการเขียนโปรแกรมอื่น ๆ เชลล์ไม่รู้จักคำสงวนทุกที่เฉพาะเมื่อมันเป็นคำแรกของคำสั่ง (มีรายละเอียดปลีกย่อยเล็กน้อย)
Gilles 'หยุดความชั่วร้าย' ใน

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

5

คุณสามารถตรวจสอบเชลล์สคริปต์ของคุณได้อย่างง่ายดายโดยใช้ShellCheckออนไลน์ (มีให้ในแบบสแตนด์อโลน)

ในกรณีนี้มันจะชี้ให้เห็นว่าคำสั่ง if- ต้องการช่องว่างหลัง[และก่อน]และว่าคุณต้องการ;(หรือขึ้นบรรทัดใหม่) ก่อนthenอยู่ในบรรทัดเดียวกัน

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

นอกจากนี้ยังบอกให้คุณใช้read -rเพื่อหยุดการreadmangling \(อาจมีความสำคัญสำหรับรหัสผ่าน) และคุณควรอ้างอิงตัวแปรเมื่อเรียกsqlplusเพื่อป้องกันเชลล์ไม่ให้ทำชื่อไฟล์กลมและแยกคำออกโดยไม่ตั้งใจ (นี่เป็นสิ่งสำคัญหาก ตัวอย่างเช่นรหัสผ่านมีอักขระตัวกลมไฟล์เช่น*หรือช่องว่าง)

การเยื้องรหัสจะทำให้อ่านง่ายขึ้นเช่นกัน:

#!/bin/bash

read -r -p 'please enter username: ' user_name
IFS= read -rs -p 'please enter password: ' pass

printf 'ORACLE_SID = %s\n' "$ORACLE_SID"
sid=$ORACLE_SID

if [ "$sid" = 'Test' ]; then
    echo 'Cannot copy' >&2
    exit 1
fi

sqlplus -s -l "$user_name/$pass@$sid" <<'SQL_END'
copy from scott/tiger@orcl insert EMP using select * from EMP
exit
SQL_END

ที่นี่ฉันได้ทำให้มันเป็นไปได้ที่จะใช้รหัสผ่านที่มีอักขระนำหน้าหรือต่อท้ายช่องว่างโดยการตั้งค่าIFSเป็นสตริงว่างชั่วคราวสำหรับการอ่านรหัสผ่านreadเป็นสตริงว่างสำหรับการอ่านรหัสผ่าน

ตรรกะก็เปลี่ยนไปประกันตัวออกมาถ้า$ORACLE_SID/ เป็น$sid Testสิ่งนี้หลีกเลี่ยงการมีส่วนการดำเนินงานหลักของสคริปต์ในifสาขา


โปรดทราบว่าif ([ x = x ]) then (echo yes) fiยังใช้งานได้
Stéphane Chazelas

@ StéphaneChazelasอ่าใช่ และนั่นอาจเป็นรูปแบบที่น่าสนใจในมุมมองของโปรแกรมที่สร้างรหัสเชลล์ แต่ไม่ใช่ว่าจะเขียนifคำสั่งด้วย[ ... ]... :-)
Kusalananda

2

เมื่อเขียนshคุณต้องการ

if [ "$ORACLE_SID" != "Test" ]
then
  ...
fi

เมื่อเขียน bash

if [[ "$ORACLE_SID" != "Test" ]]
then
  ...
fi

รังเกียจช่องว่างโปรด จะต้องมีช่องว่างระหว่าง[[และตัวดำเนินการแรก

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