Jak napisać skrypt bash, który pobiera opcjonalne argumenty wejściowe?

471

Chcę, aby mój skrypt mógł pobierać opcjonalne dane wejściowe,

np. obecnie mój skrypt to

#!/bin/bash
somecommand foo

ale chciałbym powiedzieć:

#!/bin/bash
somecommand  [ if $1 exists, $1, else, foo ]
Abe
źródło
3
Bash czy POSIX? Dzięki Bash istnieje więcej możliwości
użytkownik123444555621
@ Pumbaa80 bash - Zaktualizowałem tagi.
Abe

Odpowiedzi:

705

Możesz użyć składni wartości domyślnej:

somecommand ${1:-foo}

Powyższe będzie, jak opisano w Bash Reference Manual - 3.5.3 Rozbudowa parametru powłoki [moje podkreślenie]:

Jeśli parametr jest nieustawiony lub zerowy , rozwinięcie słowa jest podstawiane. W przeciwnym razie wartość parametru zostanie podstawiona.

Jeśli chcesz zastąpić wartość domyślną tylko wtedy, gdy parametr jest nieustawiony (ale nie jeśli jest pusty, np. Jeśli nie jest pustym ciągiem), użyj następującej składni:

somecommand ${1-foo}

Ponownie z podręcznika użytkownika Bash - 3.5.3 Rozszerzenie parametru powłoki :

Pominięcie dwukropka powoduje tylko test parametru, który nie jest ustawiony. Innymi słowy, jeśli dwukropek jest uwzględniony, operator sprawdza istnienie obu parametrów i czy jego wartość nie jest zerowa; jeśli dwukropek zostanie pominięty, operator sprawdza tylko istnienie.

Itay Perl
źródło
52
Zwróć uwagę na różnicę semantyczną między powyższym poleceniem: „return fooif $1is unset or a empty string ”, a ${1-foo}„returnfoo jeśli nie $1jest ustawiony”.
l0b0
12
Czy możesz wyjaśnić, dlaczego to działa? Jaka jest funkcja / cel „:” i „-”?
jwien001
8
@ jwein001: W powyższej odpowiedzi operator podstawienia jest używany do zwrócenia wartości domyślnej, jeśli zmienna jest niezdefiniowana. W szczególności logika brzmi: „Jeśli 1 $ istnieje i nie jest pusty, zwróć jego wartość; w przeciwnym razie zwróć foo.” Dwukropek jest opcjonalny. Jeśli zostanie pominięty, zmień „istnieje i nie ma wartości null” na „tylko istnieje”. Znak minus określa zwrot foo bez ustawiania 1 $ równego „foo”. Operatory podstawienia są podklasą operatorów ekspansji. Patrz sekcja 6.1.2.1 Robbins and Beebe's Classic Shell Scripting [O'Reilly] ( shop.oreilly.com/product/9780596005955.do )
Jubbles,
5
@Jubbles lub jeśli nie chcesz kupować całej książki dla prostego odniesienia ... tldp.org/LDP/abs/html/parameter-substitution.html
Ohad Schneider
2
Ta odpowiedź byłaby jeszcze lepsza, gdyby pokazała, jak ustawić domyślną wartość w wyniku uruchomienia polecenia, podobnie jak @hagen (choć ta odpowiedź jest nieelegancka).
sautedman
309

Możesz ustawić wartość domyślną dla zmiennej takiej jak:

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"

Oto kilka przykładów tego, jak to działa:

$ ./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
Brad Parks
źródło
2
Ach, okej -Mylić mnie (to jest negowane?).
Raffi Khatchadourian
5
Nie - to dziwny sposób, w jaki bash wykonuje swoje zadanie. Dodam jeszcze kilka przykładów, aby to trochę wyjaśnić ... dzięki!
Brad Parks
44
if [ ! -z $1 ] 
then 
    : # $1 was given
else
    : # $1 was not given
fi
Irit Katriel
źródło
1
Technicznie rzecz biorąc, jeśli podasz pusty ciąg znaków, który może być liczony jako parametr, ale twój czek go nie zauważy. W takim przypadku $ # powiedziałoby, ile parametrów podano
vmpstr
18
-njest taki sam jak ! -z.
l0b0
Uzyskać różne wyniki przy użyciu -na ! -zwięc chciałbym powiedzieć, że nie jest w tym przypadku.
Eliezer
ponieważ nie podałeś zmiennej, [ -n $1 ] zawsze będzie to prawda . Jeśli użyjesz bash, [[ -n $1 ]]zachowa się tak, jak się spodziewasz, w przeciwnym razie musisz zacytować[ -n "$1" ]
glenn jackman
21

Możesz sprawdzić liczbę argumentów za pomocą $#

#!/bin/bash
if [ $# -ge 1 ]
then
    $1
else
    foo
fi
Dennis
źródło
10

nie zapomnij, jeśli jego zmienna $ 1 .. $ n musisz zapisać do zmiennej zwykłej, aby użyć podstawienia

#!/bin/bash
NOW=$1
echo  ${NOW:-$(date +"%Y-%m-%d")}
hagen
źródło
2
Powyższa odpowiedź Brada dowodzi, że zmienne argumentów można również podstawiać bez zmiennych pośrednich.
Vadzim
2
+1 za zanotowanie sposobu użycia polecenia typu data jako wartości domyślnej zamiast stałej wartości. Jest to również możliwe:DAY=${1:-$(date +%F -d "yesterday")}
Garren S
3

W przypadku opcjonalnych wielu argumentów, analogicznie do lspolecenia, które może pobrać jeden lub więcej plików lub domyślnie wyświetla listę wszystkich elementów w bieżącym katalogu:

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

Niestety, nie działa poprawnie dla plików ze spacjami w ścieżce. Nie wymyśliłem jeszcze, jak to zrobić.

Jesse Glick
źródło
2

Umożliwia to domyślną wartość dla opcjonalnego pierwszego argumentu i zachowuje wiele argumentów.

 > cat mosh.sh
   set -- ${1:-xyz} ${@:2:$#} ; echo $*    
 > mosh.sh
   xyz
 > mosh.sh  1 2 3
   1 2 3 
mosh
źródło
1

Możliwe jest użycie podstawienia zmiennej w celu podstawienia stałej wartości lub polecenia (podobnego date) dla argumentu. Odpowiedzi do tej pory koncentrowały się na ustalonych wartościach, ale tego użyłem, aby data była opcjonalnym argumentem:

~$ 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
Garren S.
źródło