Przetwarzaj wszystkie argumenty oprócz pierwszego (w skrypcie bash)

444

Mam prosty skrypt, w którym pierwszy argument jest zarezerwowany dla nazwy pliku, a wszystkie inne opcjonalne argumenty powinny zostać przekazane do innych części skryptu.

Korzystając z Google, znalazłem tę wiki , ale podałem dosłowny przykład:

echo "${@: -1}"

Nie mogę zmusić niczego innego do pracy, takiego jak:

echo "${@:2}"

lub

echo "${@:2,1}"

Otrzymuję komunikat „Złe zastąpienie” z terminala.

Na czym polega problem i jak mogę przetwarzać wszystkie oprócz pierwszego argumentu przekazanego do skryptu bash?

theta
źródło
21
Aby zawołać innych, którzy zostali zdezorientowani, podano niewłaściwy shebang, który spowodował, "{@:2}"że nie działa, dlatego poprawna odpowiedź pasuje powyżej.
Guvante,
3
Właśnie użyłeś domyślnej powłoki, którą jest dash na Ubuntu i wielu innych Linuxach. W myślniku „$ {@: -1}” jest interpretowany jako: {parametr: -word} - Użyj wartości domyślnych i użyj słowa, jeśli parametr nie jest zdefiniowany lub jest pusty. Tak więc myślnik „$ {@: -1}” daje dokładnie takie same wyniki jak „$ @”. Aby użyć bash, użyj następującego pierwszego wiersza w pliku skryptu: #! / Bin / bash
luart

Odpowiedzi:

660

Użyj tego:

echo "${@:2}"

Następująca składnia:

echo "${*:2}"

działałoby również, ale nie jest zalecane, ponieważ, jak już wyjaśniono @Gordon , że używając *, uruchamia wszystkie argumenty razem jako pojedynczy argument ze spacjami, @zachowując przerwy między nimi (nawet jeśli niektóre same argumenty zawierają spacje ). Nie robi różnicy echo, ale ma znaczenie dla wielu innych poleceń.

Oliver Charlesworth
źródło
7
Właśnie zdałem sobie sprawę, że mój shebang był zły: #!/usr/bin/env shdlatego miałem problemy. Twój przykład działa dobrze, tak jak powyżej, pod warunkiem, że usunąłem ten shebang
theta
110
Użyj "${@:2}"zamiast tego - użycie *uruchamia wszystkie argumenty razem jako pojedynczy argument ze spacjami, @zachowując przerwy między nimi (nawet jeśli same argumenty zawierają spacje). Różnica nie jest zauważalna echo, ale ma znaczenie dla wielu innych rzeczy.
Gordon Davisson
2
@GordonDavisson Chodzi o to, aby argumentować razem. Gdybyśmy przekazywali nazwy plików, miałbyś rację. echojest wystarczająco wybaczający, aby połączyć je dla was; inne polecenia mogą nie być tak miłe. Nie używaj tylko jednego lub drugiego: poznaj różnicę między *i @i kiedy z nich korzystać. Powinieneś ich używać w równym stopniu. Dobry przykład, kiedy będzie to stanowić problem: jeśli $3zawiera podział wiersza ( \n), zostanie zastąpiony spacją, pod warunkiem, że masz $IFSzmienną domyślną .
Zenexer
1
@Zenexer: Jak rozumiem, pytanie było jak przekazać wszystko ale pierwszy argument do „drugiej strony skryptu”, z echojedynie jako przykład - w takim przypadku powinny one nie być uruchamiane razem. Z mojego doświadczenia wynika , że sytuacje, w których chcesz, aby działały razem, są rzadkie (patrz to pytanie na przykład ) i "$@"prawie zawsze są tym, czego chcesz. Ponadto problem, o którym wspominasz przy podziale wiersza, występuje tylko wtedy, gdy $@(lub $*) nie występuje w cudzysłowie.
Gordon Davisson,
@ GordonDavisson Hmm ... masz rację, teraz, kiedy o tym myślę; przerwa w linii nie powinna stanowić problemu. Musiałem myśleć o czymś innym. Jednak nadal nie mogę się nie zgodzić na ich wspólne prowadzenie; Muszę używać $*dość często w moich skryptach.
Zenexer,
180

Jeśli potrzebujesz rozwiązania, które działa również w /bin/shtry

first_arg="$1"
shift
echo First argument: "$first_arg"
echo Remaining arguments: "$@"

shift [n]przesuwa parametry pozycyjne n razy. A shiftustawia wartość $1na wartość $2, wartość $2na wartość $3itd., Zmniejszając wartość o $#jeden.

Ben Jackson
źródło
25
+1: Prawdopodobnie powinieneś zaoszczędzić 1 $ na zmiennej przed przesunięciem.
glenn jackman
3
Zaskakująco foo=shiftnie robi tego, czego się spodziewałam.
Keith Smiley
Wiem, że to stare, ale spróbujfoo=$(shift)
raumaan kidwai
12
@raumaankidwai To nie działa z 2 powodów: 1) shift(w powłoce) nie ma żadnych danych wyjściowych. Po prostu $1wszystko odrzuca i przesuwa. 2) $(...)uruchamia podpowłokę, która ma własne lokalne argumenty. Przesuwa argumenty w podpowłoce, co nie wpływa na rodzica
Ben Jackson
Dzięki :) Czy jest jakiś sposób na zrobienie tego, co somecommand "$1" "${@:2}"robi ta metoda (tj. Przesunięcie „inline”)?
Anonimowy,
0

Natknąłem się na to, szukając czegoś innego. Podczas gdy post wygląda na dość stary, najłatwiejsze rozwiązanie w bash jest zilustrowane poniżej (przynajmniej bash 4), set -- "${@:#}"gdzie gdzie # to numer początkowy elementu tablicy, który chcemy zachować do przodu:

    #!/bin/bash

    someVar="${1}"
    someOtherVar="${2}"
    set -- "${@:3}"
    input=${@}

    [[ "${input[*],,}" == *"someword"* ]] && someNewVar="trigger"

    echo -e "${someVar}\n${someOtherVar}\n${someNewVar}\n\n${@}"

Zasadniczo, po set -- "${@:3}"prostu wyskakuje z pierwszych dwóch elementów w tablicy, takich jak przesunięcie Perla i zachowuje wszystkie pozostałe elementy, w tym trzeci. Podejrzewam, że jest też sposób na zerwanie ostatnich elementów.

Pavman
źródło