เหตุใดจึงเลือกสาขา 'ถ้า [$ 1 =“ 1”]' เสมอแม้ว่าจะไม่ใช่ $ 1 ก็ตาม


10

ฉันมีเชลล์สคริปต์ชื่อ 'teleport.sh' เช่นนี้:

if [ $1="1" ];
    then
    shift
        mv "$@" ~/lab/Sun
elif [ $1="2" ];
    then
    shift
        mv "$@" ~/lab/Moon
elif [ $1="3" ];
    then
    shift
        mv "$@" ~/lab/Earth
fi

เมื่อฉันรัน:

sh teleport.sh 2 testfile

สิ่งนี้testfileถูกย้ายไปยัง~/lab/Sunไดเรกทอรีซึ่งทำให้ฉันสับสนมากเพราะฉันไม่ผ่าน 1 หรือ '1' ไปยังสคริปต์นั้น

เกิดอะไรขึ้นที่นี่?


1
+1 สำหรับห้องปฏิบัติการ , ดวงอาทิตย์ , ดวงจันทร์ , โลกและส่งผ่านทางไกล แต่คุณควรเสมอคู่ขยายคำพูด ( $var, $(cmd)และแม้กระทั่ง`cmd`[ที่$(cmd)ควรได้รับการแนะนำ]) มีบางกรณีที่คุณไม่จำเป็นต้องพูด แต่จะทำไม่ได้เจ็บ
nyuszika7h

@ nyuszika7h ไม่ควรใส่เครื่องหมายคำพูดคู่หมายถึง "$ var" และ "$ cmd"? ประโยชน์ของวงเล็บกลมที่คุณกล่าวถึงข้างต้นคืออะไร
เซน

$(cmd)คือแทนคำสั่ง (ส่วนใหญ่) `cmd`เช่นเดียวกับ ดูmywiki.wooledge.org/CommandSubstitutionและmywiki.wooledge.org/BashFAQ/082
nyuszika7h

คำตอบ:


19

การใช้ช่องว่างช่วยแก้ไขปัญหาของคุณ

if [ "$1" = 1 ];
    then
    shift
        mv "$@" ~/lab/Sun
elif [ "$1" = 2 ];
    then
    shift
        mv "$@" ~/lab/Moon
elif [ "$1" = 3 ];
    then
    shift
        mv "$@" ~/lab/Earth
fi

แม้ว่านี่จะเป็นตอนสุดท้าย:

#!/bin/bash

action=$1
shift
files=("$@")
case $action in  
  1) mv -- "${files[@]}" ~/lab/Sun     ;;
  2) mv -- "${files[@]}" ~/lab/Moon    ;;
  3) mv -- "${files[@]}" ~/lab/Earth   ;;
esac

3
ใช่ช่องว่างนั้นมีความจำเป็น แต่ฉันสงสัยว่าทำไม$1="1"รูปแบบดั้งเดิม"ทำงาน" เลย (ให้ผลลัพธ์ที่แท้จริง) อย่างไร[/ testbuiltin จริงตีความการแสดงออกที่?
echristopherson

5
@echristopherson ไม่มีช่องว่างtestเห็นอาร์กิวเมนต์เดียวเท่านั้น ("l-value") หากไม่มีข้อโต้แย้งอื่น ๆ ("โอเปอเรเตอร์" และ "r-value") ไม่มีอะไรต้องทดสอบดังนั้นtestเพียงแค่พูดว่า "โอเคใช่คุณได้ให้อะไรบางอย่างกับฉัน
อธิการ

2
$1="1"ถูก$1ตัดแบ่งโดย=1
jdh8

เมื่อใช้เคสวิธีการตั้งค่าแอ็คชันเพื่อดำเนินการเมื่อไม่มีเงื่อนไขตรงตามเงื่อนไข?
เซน

11

สิ่งที่เห็นได้ชัดเป็นครั้งแรกที่คุณควรให้ช่องว่างระหว่างข้อโต้แย้งของ[, testหรือ[[:

if [ "$1" = 1 ];

เมื่ออยู่ใน Bash [[ ]]แนะนำให้ใช้เนื่องจากจะไม่ทำสิ่งที่ไม่จำเป็นสำหรับการแสดงออกตามเงื่อนไขเช่นการแยกคำและการขยายชื่อพา ธ ไม่จำเป็นต้องมีการเสนอราคา ผู้ประกอบการอ่านได้มากขึ้น==นอกจากนี้ยังสามารถนำมาใช้

if [[ $1 == 1 ]];

เพิ่มหมายเหตุ: หากถูกดำเนินการที่สองนอกจากนี้ยังมีตัวแปรที่อ้างเป็นสิ่งที่จำเป็นในขณะที่มันอาจจะมีการจับคู่รูปแบบถ้ามันมีตัวละครที่เป็นที่รู้จักเช่น*, ?, []ฯลฯ .. ถ้าขยาย globbing หรือจับคู่รูปแบบการใช้งานด้วยshopt -s extglobรูปแบบอื่น ๆ ชอบ@(), !()ฯลฯ จะได้รับการยอมรับว่าเป็นรูปแบบ ดูรูปแบบการจับคู่

ด้วยโอเปอเรเตอร์ที่ชอบ<และ>อาจยังจำเป็นเพราะฉันเคยพบข้อผิดพลาดที่ไม่ได้อ้างถึงข้อโต้แย้งที่สองทำให้เกิดผลลัพธ์ที่แตกต่างกัน

สำหรับตัวถูกดำเนินการแรกไม่มีสิ่งใดนำไปใช้

พิจารณารูปแบบที่ง่ายกว่านี้เช่นกัน:

case "$1" in
1)
    mv -- "${@:2}" ~/lab/Sun
    ;;
2)
    mv -- "${@:2}" ~/lab/Moon
    ;;
3)
    mv -- "${@:2}" ~/lab/Earth
    ;;
esac

หรือย่อ:

case "$1" in
1) mv -- "${@:2}" ~/lab/Sun ;;
2) mv -- "${@:2}" ~/lab/Moon ;;
3) mv -- "${@:2}" ~/lab/Earth ;;
esac

"${@:2}"เป็นรูปแบบของการขยายสตริงย่อยหรือการขยายสมาชิกอาร์เรย์โดยที่2ออฟเซ็ต สิ่งนี้ทำให้การขยายเริ่มต้นที่ค่าที่สอง shiftด้วยวิธีนี้เราอาจไม่จำเป็นต้องใช้

การ--ป้องกันที่เพิ่มเข้ามาmvจากการรู้จักชื่อไฟล์ที่ขึ้นต้นด้วยขีด ( -) เป็นตัวเลือกที่ไม่ถูกต้อง


คุณไม่ควรแตกในแต่ละกรณี?
Archemar

2
@Archemar: ไม่ไม่มีการล้ม (ตรงกันข้ามกับภาษาอื่น ๆ มากมาย)
Mat

2
@ แมทมันมีความล้มเหลวถ้าคุณใช้;&แทน;;(ksh, bash, zsh เท่านั้น) แต่ก็breakยังไม่ได้ป้องกันการตกผ่านbreakเป็นเพียงการแยกออกจากลูป
Stéphane Chazelas

นั่นไม่ใช่ข้อโต้แย้งifแต่เป็น[คำสั่ง
Stéphane Chazelas

1
การแก้ไข: ไม่ต้องใช้ เครื่องหมายคำพูดคู่ทางด้านซ้ายมือ! [[ $foo == $bar ]]จะทำการจับคู่รูปแบบ แต่[[ $foo == "$bar" ]]จะไม่
nyuszika7h

7

เพื่อตอบคำถามว่าทำไมสิ่งนี้จึงเกิดขึ้นลักษณะการทำงานของ[aka testนี้ได้รับการบันทึกไว้ใน POSIX :

ในรายการต่อไปนี้ $ 1, $ 2, $ 3 และ $ 4 แสดงถึงข้อโต้แย้งที่นำเสนอเพื่อทดสอบ:

[ ... ]

1 อาร์กิวเมนต์:

ออกจริง (0) ถ้า $ 1 ไม่ใช่โมฆะ; มิฉะนั้นให้ออกจาก false

คุณกำลังผ่านการโต้แย้ง 1 ข้อ2=1ซึ่งไม่เป็นโมฆะดังนั้นจึงtestออกมาพร้อมกับความสำเร็จ

ในฐานะที่เป็นกระทู้อื่น ๆ (และshellcheck ) ชี้ให้เห็นว่าถ้าคุณต้องการที่จะเปรียบเทียบเพื่อความเท่าเทียมกันคุณจะแทนที่จะต้องผ่าน 3 ข้อโต้แย้ง2, และ=1


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

1

ฉันแค่อยากจะแนะนำทางเลือกแบบพกพาและตัวเลือกอื่น ๆ Bash ไม่ใช่ universal (และถ้าคุณไม่ต้องการ universal ทำไมคุณถึงเขียนเชลล์สคริปต์?)

#! /bin/sh
action="$1"
shift
case "$action" in
    1) dest=Sun   ;;
    2) dest=Moon  ;;
    3) dest=Earth ;;
    *) echo "Unrecognized action code '$action' (must be 1, 2, or 3)" >&2; exit 1 ;;
esac
mv -- "$@" ~/lab/"$dest"

(หมายเหตุ pedants: ใช่ฉันรู้ว่าคำพูดที่อยู่รอบ ๆ$actionบนcase "$action" inเส้นที่ไม่จำเป็น แต่ผมรู้สึกว่ามันเป็นดีที่สุดที่จะทำให้พวกเขามีอยู่แล้วเพื่อให้ผู้อ่านในอนาคตจะได้ไม่ต้องจำไว้ว่า.)


1
"Bash ไม่ใช่สากล (และถ้าคุณไม่ต้องการ universal ทำไมคุณถึงเขียนเชลล์สคริปต์?)" - นี่ดูเหมือนจะบอกเป็นนัยว่า bash scripting ไม่มีกรณีการใช้งาน
Ruslan

@Ruslan ใช่ว่าเป็นความเห็นของฉัน เขียน/bin/shสคริปต์แบบพกพาหากคุณต้องการ; มิฉะนั้นให้เขียนด้วยภาษาสคริปต์ที่แย่กว่าเชลล์ ล่าม Perl พื้นฐานในความเป็นจริงมีแนวโน้มที่จะมีอยู่ในสภาพแวดล้อมที่เป็นกรรมสิทธิ์ดั้งเดิมและสภาพแวดล้อมที่ฝังตัวลดลงกว่าทุบตีคือ
zwol
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.