วิธีเขียนสคริปต์ทุบตีที่รับอาร์กิวเมนต์อินพุตเสริมหรือไม่?


471

ฉันต้องการให้สคริปต์ของฉันสามารถเลือกอินพุตได้

เช่นปัจจุบันสคริปต์ของฉันคือ

#!/bin/bash
somecommand foo

แต่ฉันอยากจะบอกว่า:

#!/bin/bash
somecommand  [ if $1 exists, $1, else, foo ]

3
ทุบตีหรือ POSIX? ด้วย Bash มีความเป็นไปได้มากขึ้น
user123444555621

@ Pumbaa80 bash - ฉันได้อัปเดตแท็กแล้ว
Abe

1
ดูเพิ่มเติมที่unix.stackexchange.com/questions/122845/…
Vadzim

คำตอบ:


706

คุณสามารถใช้ไวยากรณ์ค่าเริ่มต้น:

somecommand ${1:-foo}

ข้างต้นจะอธิบายไว้ในทุบตี Reference Manual - 3.5.3 ขยายเชลล์พารามิเตอร์ [ผมขอย้ำ]:

หากพารามิเตอร์ไม่ได้ตั้งค่าหรือเป็นโมฆะการขยายตัวของคำจะถูกแทนที่ มิฉะนั้นค่าของพารามิเตอร์จะถูกแทนที่

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

somecommand ${1-foo}

อีกครั้งจากคู่มืออ้างอิง Bash - การขยายพารามิเตอร์เชลล์ 3.5.3 :

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


52
โปรดทราบความแตกต่างทางความหมายระหว่างคำสั่งข้างต้น "คืนfooถ้า$1ไม่ได้ตั้งค่าหรือสตริงว่าง " และ${1-foo}"กลับมาfooถ้า$1ไม่ได้ตั้งค่า"
l0b0

12
คุณช่วยอธิบายได้ไหมว่าทำไมสิ่งนี้ถึงได้ผล ฟังก์ชั่น / วัตถุประสงค์ของ ':' และ '-' เป็นพิเศษคืออะไร
jwien001

8
@ jwein001: ในคำตอบที่ส่งมาผู้ประกอบการทดแทนจะใช้ในการส่งกลับค่าเริ่มต้นหากตัวแปรไม่ได้กำหนด โดยเฉพาะอย่างยิ่งตรรกะคือ "ถ้ามีอยู่ $ 1 และไม่เป็นโมฆะให้ส่งกลับค่ามิฉะนั้นส่งคืน foo ลำไส้ใหญ่เป็นตัวเลือก หากไม่ได้ระบุไว้ให้เปลี่ยน "มีอยู่และไม่เป็นโมฆะ" เป็น "มีอยู่เท่านั้น" เครื่องหมายลบระบุว่าจะส่งคืน foo โดยไม่ตั้งค่า $ 1 เท่ากับ 'foo' ผู้ประกอบการทดแทนเป็น subclass ของผู้ประกอบการขยายตัว ดูหัวข้อ 6.1.2.1 ของ Robbins และ Beebe's Classic Shell Scripting [O'Reilly] ( shop.oreilly.com/product/9780596005955.do )
Jubbles

5
@ Jubbles หรือหากคุณไม่ต้องการซื้อหนังสือทั้งเล่มสำหรับการอ้างอิงอย่างง่าย ๆ ... tldp.org/LDP/abs/html/parameter-substitution.html
Ohad Schneider

2
คำตอบนี้จะดียิ่งขึ้นถ้ามันแสดงวิธีการเริ่มต้นเป็นผลมาจากการใช้คำสั่งตามที่ @hagen ทำ (แม้ว่าคำตอบนั้นไม่เหมาะสม)
sautedman

310

คุณสามารถตั้งค่าเริ่มต้นสำหรับตัวแปรดังนี้:

somecommand.sh

#!/usr/bin/env bash

ARG1=${1:-foo}
ARG2=${2:-bar}
ARG3=${3:-1}
ARG4=${4:-$(date)}

echo "$ARG1"
echo "$ARG2"
echo "$ARG3"
echo "$ARG4"

นี่คือตัวอย่างของวิธีการทำงาน:

$ ./somecommand.sh
foo
bar
1
Thu Mar 29 10:03:20 ADT 2018

$ ./somecommand.sh ez
ez
bar
1
Thu Mar 29 10:03:40 ADT 2018

$ ./somecommand.sh able was i
able
was
i
Thu Mar 29 10:03:54 ADT 2018

$ ./somecommand.sh "able was i"
able was i
bar
1
Thu Mar 29 10:04:01 ADT 2018

$ ./somecommand.sh "able was i" super
able was i
super
1
Thu Mar 29 10:04:10 ADT 2018

$ ./somecommand.sh "" "super duper"
foo
super duper
1
Thu Mar 29 10:05:04 ADT 2018

$ ./somecommand.sh "" "super duper" hi you
foo
super duper
hi
you

2
อาโอเค. -สับสนฉัน (มันเมื่อตะกี้?)
Raffi Khatchadourian

5
Nope - นั่นเป็นเพียงวิธีการทุบตีแปลก ๆ ในการทำงานที่ได้รับมอบหมาย ฉันจะเพิ่มตัวอย่างเพิ่มเติมเพื่อชี้แจงนี้เล็กน้อย ... ขอบคุณ!
Brad Parks

44
if [ ! -z $1 ] 
then 
    : # $1 was given
else
    : # $1 was not given
fi

1
ในทางเทคนิคหากคุณส่งผ่านสตริงว่าง '' ที่อาจนับเป็นพารามิเตอร์ได้ แต่เช็คของคุณจะพลาด ในกรณีนี้ $ # จะบอกว่าได้รับพารามิเตอร์กี่ตัว
vmpstr

18
-n! -zเป็นเช่นเดียวกับ
l0b0

ฉันได้รับผลลัพธ์ที่แตกต่างกันโดยใช้-nและ! -zดังนั้นฉันจะบอกว่าไม่ได้เป็นกรณีที่นี่
Eliezer

เนื่องจากคุณไม่สามารถอ้างอิงตัวแปรได้[ -n $1 ] จะเป็นจริงเสมอ หากคุณใช้ทุบตี[[ -n $1 ]]จะทำงานตามที่คุณคาดหวังมิฉะนั้นคุณต้องพูด[ -n "$1" ]
เกล็นแจ็คแมน


10

โปรดอย่าลืมถ้าตัวแปร $ 1 .. $ n คุณต้องเขียนถึงตัวแปรปกติเพื่อใช้การทดแทน

#!/bin/bash
NOW=$1
echo  ${NOW:-$(date +"%Y-%m-%d")}

2
คำตอบของแบรดข้างต้นพิสูจน์ได้ว่าตัวแปรอาร์กิวเมนต์สามารถใช้แทนค่ากลางได้
Vadzim

2
+1 สำหรับสังเกตวิธีใช้คำสั่งเช่นวันที่เป็นค่าเริ่มต้นแทนที่จะเป็นค่าคงที่ นอกจากนี้ยังเป็นไปได้:DAY=${1:-$(date +%F -d "yesterday")}
Garren S

3

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

if [ $# -ge 1 ]
then
    files="$@"
else
    files=*
fi
for f in $files
do
    echo "found $f"
done

ไม่ทำงานอย่างถูกต้องสำหรับไฟล์ที่มีช่องว่างในเส้นทางอนิจจา ยังไม่ได้คิดวิธีการทำงานที่ยัง


2

สิ่งนี้อนุญาตให้มีค่าเริ่มต้นสำหรับ ARG ตัวเลือกที่ 1 และเก็บรักษาไว้หลาย args

 > cat mosh.sh
   set -- ${1:-xyz} ${@:2:$#} ; echo $*    
 > mosh.sh
   xyz
 > mosh.sh  1 2 3
   1 2 3 

1

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

~$ sh co.sh
2017-01-05

~$ sh co.sh 2017-01-04
2017-01-04

~$ cat co.sh

DAY=${1:-$(date +%F -d "yesterday")}
echo $DAY
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.