การส่งอาร์กิวเมนต์ที่กำหนดชื่อไปยังสคริปต์ของเชลล์


114

มีวิธีง่ายๆในการส่งผ่าน (รับ) พารามิเตอร์ชื่อไปยังสคริปต์เชลล์หรือไม่?

ตัวอย่างเช่น,

my_script -p_out '/some/path' -arg_1 '5'

และภายในmy_script.shรับพวกเขาเป็น:

# I believe this notation does not work, but is there anything close to it?
p_out=$ARGUMENTS['p_out']
arg1=$ARGUMENTS['arg_1']

printf "The Argument p_out is %s" "$p_out"
printf "The Argument arg_1 is %s" "$arg1"

เป็นไปได้ใน Bash หรือ Zsh?


2
ดูdocopt - ช่วยด้วยพารามิเตอร์ที่มีชื่อและทำการตรวจสอบอินพุตเช่นกัน
เอาชนะ

ที่เกี่ยวข้อง: stackoverflow.com/questions/5499472/…
Kaz

คำตอบ:


34

ไวยากรณ์ที่ใกล้เคียงที่สุดน่าจะเป็น:

p_out='/some/path' arg_1='5' my_script

7
ที่เกี่ยวข้องกับสิ่งนี้ถ้า-kตัวเลือกถูกตั้งค่าในการเรียกเปลือกแล้วmy_script p_out='/some/path' arg_1='5'มีผลเช่นเดียวกัน (ข้อโต้แย้งทั้งหมดในรูปแบบของการมอบหมายจะถูกเพิ่มลงในสภาพแวดล้อมไม่ใช่แค่การมอบหมายก่อนหน้าคำสั่ง)
chepner

13
ฉันเคยรักไวยากรณ์นี้ แต่มันมีข้อแม้ใหญ่: หลังจากดำเนินการคำสั่ง / ฟังก์ชั่นตัวแปรเหล่านั้นจะยังคงถูกกำหนดในขอบเขตปัจจุบัน! เช่น: x=42 echo $x; echo $xซึ่งหมายความว่าในการดำเนินการครั้งต่อไปของmy_scriptถ้าp_outถูกละไว้มันจะยึดติดกับค่าที่ผ่านไปในครั้งสุดท้าย !! ( '/some/path')
Lucas Cimon

@LucasCimon คุณไม่สามารถทำได้unsetหลังจากการประหารครั้งแรกรีเซ็ตพวกมันก่อนการประหารครั้งต่อไปหรือไม่?
Nikos Alexandris

2
@LucasCimon นั่นไม่ถูกต้อง x=42 echo $xไม่ได้เอาท์พุทอะไรเลยหาก$xไม่ได้กำหนดไว้ก่อน
Hauke ​​Laging

คุณถูกต้อง @HaukeLaging ขอบคุณสำหรับการแก้ไข
Lucas Cimon

147

หากคุณไม่รังเกียจที่จะ จำกัด เฉพาะชื่ออาร์กิวเมนต์ตัวอักษรตัวเดียวนั่นmy_script -p '/some/path' -a5ก็คือในการทุบตีคุณสามารถใช้ built-in getoptsเช่น

#!/bin/bash

while getopts ":a:p:" opt; do
  case $opt in
    a) arg_1="$OPTARG"
    ;;
    p) p_out="$OPTARG"
    ;;
    \?) echo "Invalid option -$OPTARG" >&2
    ;;
  esac
done

printf "Argument p_out is %s\n" "$p_out"
printf "Argument arg_1 is %s\n" "$arg_1"

จากนั้นคุณสามารถทำได้

$ ./my_script -p '/some/path' -a5
Argument p_out is /some/path
Argument arg_1 is 5

มีตัวช่วยการสอนขนาดเล็ก getopts ที่เป็นประโยชน์หรือคุณสามารถพิมพ์help getoptsที่ shell prompt


25
นี่ควรเป็นคำตอบที่ยอมรับ
Kaushik Ghose

3
ฉันรู้ว่ามันค่อนข้างเก่า แต่ทำไมมีเพียง 1 ตัวอักษรสำหรับการโต้แย้ง?
เควิน

1
ฉันใช้สิ่งนี้ (แต่ด้วยiและd) เมื่อฉันเรียกใช้ด้วยmy_script -i asd -d asdฉันได้รับสตริงว่างสำหรับdอาร์กิวเมนต์ เมื่อฉันเรียกใช้my_script -d asd -i asdฉันจะได้รับสตริงว่างสำหรับอาร์กิวเมนต์ทั้งสอง
Milkncookiez

3
@Milkncookiez - ฉันมีปัญหาที่คล้ายกัน - ฉันไม่ได้รวม ':' หลังจากการโต้แย้งครั้งสุดท้าย ('w' ในกรณีของฉัน) เมื่อฉันเพิ่ม ':' มันเริ่มทำงานตามที่คาดไว้
Derek

37

ฉันขโมยสิ่งนี้จากdrupal.orgแต่คุณสามารถทำสิ่งนี้:

while [ $# -gt 0 ]; do
  case "$1" in
    --p_out=*)
      p_out="${1#*=}"
      ;;
    --arg_1=*)
      arg_1="${1#*=}"
      ;;
    *)
      printf "***************************\n"
      printf "* Error: Invalid argument.*\n"
      printf "***************************\n"
      exit 1
  esac
  shift
done

my_script --p_out=/some/path --arg_1=5ข้อแม้เพียงว่าคุณต้องใช้ไวยากรณ์


7
ข้อแม้นั้นไม่จำเป็น :) คุณสามารถมีเงื่อนไขดังต่อไปนี้:-c|--condition)
Milkncookiez

28

ฉันใช้สคริปต์นี้และทำงานเหมือนเครื่องราง:

for ARGUMENT in "$@"
do

    KEY=$(echo $ARGUMENT | cut -f1 -d=)
    VALUE=$(echo $ARGUMENT | cut -f2 -d=)   

    case "$KEY" in
            STEPS)              STEPS=${VALUE} ;;
            REPOSITORY_NAME)    REPOSITORY_NAME=${VALUE} ;;     
            *)   
    esac    


done

echo "STEPS = $STEPS"
echo "REPOSITORY_NAME = $REPOSITORY_NAME"

การใช้

bash my_scripts.sh  STEPS="ABC" REPOSITORY_NAME="stackexchange"

ผลลัพธ์ของคอนโซล:

STEPS = ABC
REPOSITORY_NAME = stackexchange

STEPSและREPOSITORY_NAMEพร้อมใช้งานในสคริปต์แล้ว

ไม่สำคัญว่าจะมีคำสั่งใดที่ขัดแย้งกัน


4
นี่เป็นระเบียบและควรเป็นคำตอบที่ยอมรับได้
miguelmorin

15

ด้วยzshคุณจะใช้zparseopts:

#! /bin/zsh -
zmodload zsh/zutil
zparseopts -A ARGUMENTS -p_out: -arg_1:

p_out=$ARGUMENTS[--p_out]
arg1=$ARGUMENTS[--arg_1]

printf 'Argument p_out is "%s"\n' "$p_out"
printf 'Argument arg_1 is "%s"\n' "$arg_1"

myscript --p_out fooแต่คุณต้องการเรียกสคริปต์ด้วย

โปรดทราบว่าzparseoptsไม่รองรับตัวเลือกแบบย่อหรือ--p_out=fooแบบย่อที่GNU getopt(3)ทำ


คุณรู้หรือไม่ว่าเหตุใด zparseopts จึงใช้เส้นประเพียงเส้นเดียวสำหรับการขัดแย้งในขณะที่[]เครื่องหมายขีดคั่น 2 ขีด ไม่สมเหตุสมผล!
Timo

@Timo ดูinfo zsh zparseoptsรายละเอียด
Stéphane Chazelas

9

ฉันเพิ่งมากับสคริปต์นี้

while [ $# -gt 0 ]; do

   if [[ $1 == *"--"* ]]; then
        v="${1/--/}"
        declare $v="$2"
   fi

  shift
done

ผ่านมันชอบmy_script --p_out /some/path --arg_1 5แล้วในสคริปต์คุณสามารถใช้และ$arg_1$p_out


ผมชอบวิธีนี้ใน KSH88 ผมต้องv=``echo ${1} | awk '{print substr($1,3)}'`` typeset $v="$2"(ลบหนึ่ง backtick แต่ละด้าน)
Hol

-2

หากฟังก์ชันหรือแอปพลิเคชันมีอาร์กิวเมนต์มากกว่าศูนย์จะมีอาร์กิวเมนต์สุดท้ายเสมอ

หากคุณต้องการอ่านค่าสถานะคู่และตัวเลือกค่าเช่นเดียวกับใน: $ ./t.sh -o output -i input -l last

และคุณต้องการยอมรับจำนวนตัวแปรของคู่ตัวเลือก / ค่า

และไม่ต้องการต้นไม้ขนาดใหญ่ถ้าหาก .. แล้ว .. .. .. ต้นไม้อื่น

จากนั้นหลังจากตรวจสอบหาจำนวนอาร์กิวเมนต์ที่ไม่เป็นศูนย์และสม่ำเสมอ

เขียน a while loop พร้อมกับคำสั่ง eval ทั้งสี่นี้เป็นเนื้อความตามด้วยคำสั่ง case โดยใช้ค่าสองค่าที่กำหนดในแต่ละ pass through loop

ส่วนที่ยากของการเขียนสคริปต์แสดงไว้ที่นี่:

#!/bin/sh    

# For each pair - this chunk is hard coded for the last pair.
eval TMP="'$'$#"
eval "PICK=$TMP"
eval TMP="'$'$(($#-1))"
eval "OPT=$TMP"

# process as required - usually a case statement on $OPT
echo "$OPT \n $PICK"

# Then decrement the indices (as in third eval statement) 

:<< EoF_test
$ ./t.sh -o output -i input -l last
-l 
last
$ ./t.sh -o output -l last
-l 
last
$ ./t.sh  -l last
-l 
last
EoF_test

-3
mitsos@redhat24$ my_script "a=1;b=mitsos;c=karamitsos"
#!/bin/sh
eval "$1"

คุณเพิ่งฉีดพารามิเตอร์บรรทัดคำสั่งในขอบเขตสคริปต์ !!


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