Elabora tutti gli argomenti tranne il primo (in uno script bash)


444

Ho un semplice script in cui il primo argomento è riservato al nome file e tutti gli altri argomenti facoltativi devono essere passati ad altre parti dello script.

Utilizzando Google ho trovato questo wiki , ma ha fornito un esempio letterale:

echo "${@: -1}"

Non riesco a far funzionare nient'altro, come:

echo "${@:2}"

o

echo "${@:2,1}"

Ottengo "Sostituzione errata" dal terminale.

Qual è il problema e come posso elaborare tutto tranne il primo argomento passato a uno script bash?


21
Per chiedere confusione a chiunque altro, è stato fornito lo shebang sbagliato che "{@:2}"non funzionava, motivo per cui la risposta corretta corrisponde sopra.
Guvante,

3
Hai appena usato la shell di default, che è un trattino su Ubuntu e molti altri Linux. Nel trattino "$ {@: -1}" viene interpretato come: {parametro: -word} - Usa valori predefiniti e usa word se il parametro non è definito o nullo. Quindi nel trattino "$ {@: -1}" risulta esattamente lo stesso di "$ @". Per usare bash basta usare la seguente prima riga nel file di script: #! / Bin / bash
luart

Risposte:


661

Usa questo:

echo "${@:2}"

La seguente sintassi:

echo "${*:2}"

funzionerebbe anche, ma non è raccomandato, perché come già spiegato da @Gordon , usando *, esegue tutti gli argomenti insieme come un singolo argomento con spazi, mentre @conserva le interruzioni tra loro (anche se alcuni degli argomenti stessi contengono spazi ). Non fa la differenza echo, ma è importante per molti altri comandi.


7
Ho appena realizzato che il mio shebang era cattivo: #!/usr/bin/env shecco perché ho avuto problemi. Il tuo esempio funziona benissimo, come sopra indicato, dopo che ho rimosso quel shebang
theta il

110
Usa "${@:2}"invece - using *esegue tutti gli argomenti insieme come un singolo argomento con spazi, mentre @conserva le interruzioni tra di loro (anche se alcuni degli argomenti stessi contengono spazi). La differenza non è evidente con echo, ma è importante per molte altre cose.
Gordon Davisson,

2
@GordonDavisson Il punto qui è di eseguire gli argomenti insieme. Se passassimo i nomi dei file, avresti ragione. echosta perdonando abbastanza da concatenarli per te; altri comandi potrebbero non essere così belli. Non usare solo l'uno o l'altro: impara la differenza tra *e @e quando usarli. Dovresti usarli allo stesso modo. Un buon esempio di quando questo sarà un problema: se $3contiene un'interruzione di riga ( \n), verrà sostituito con uno spazio, purché si disponga di una $IFSvariabile predefinita .
Zenexer,

1
@Zenexer: A quanto mi risulta, la domanda era come passare tutti, ma il primo argomento a "altra parte della sceneggiatura", con echoappena usato come un esempio - nel qual caso dovrebbero non essere eseguiti insieme. Nella mia esperienza, le situazioni in cui vuoi che corrano insieme sono rare (vedi questa domanda per un esempio ), ed "$@"è quasi sempre quello che vuoi. Inoltre, il problema menzionato con un'interruzione di riga si verifica solo se $@(o $*) non è tra virgolette doppie.
Gordon Davisson,

@GordonDavisson Hmm ... hai ragione, ora che ci penso; l'interruzione di riga non dovrebbe essere un problema. Devo aver pensato a qualcos'altro. Tuttavia, devo ancora non essere d'accordo sul fatto di gestirli insieme; Devo usare $*abbastanza spesso nei miei script.
Zenexer,

180

Se vuoi una soluzione che funzioni anche in /bin/shprova

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

shift [n]sposta i parametri posizionali n volte. A shiftimposta il valore di $1al valore di $2, il valore di $2al valore di $3e così via, diminuendo il valore di $#di uno.


25
+1: Probabilmente dovresti risparmiare $ 1 in una variabile prima di spostarla.
Glenn Jackman,

3
Sorprendentemente foo=shiftnon fa quello che mi aspetto.
Keith Smiley

So che è vecchio, ma provafoo=$(shift)
raumaan kidwai,

12
@raumaankidwai Questo non funziona per 2 motivi: 1) shift(nella shell) non ha alcun output. Scarta $1e sposta tutto verso il basso. 2) $(...)avvia una subshell, che ha i suoi argomenti locali. Sposta gli argomenti nella subshell, che non influisce sul genitore
Ben Jackson,

Grazie :) C'è un modo per fare ciò che somecommand "$1" "${@:2}"fa con questo metodo (cioè spostare "inline") però?
Anonimo l'


0

È venuto attraverso questo alla ricerca di qualcos'altro. Mentre il post sembra piuttosto vecchio, la soluzione più semplice in bash è illustrata di seguito (almeno bash 4) usando set -- "${@:#}"dove # è il numero iniziale dell'elemento dell'array che vogliamo preservare:

    #!/bin/bash

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

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

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

Fondamentalmente, il set -- "${@:3}"giusto espelle i primi due elementi nell'array come il turno di Perl e conserva tutti gli elementi rimanenti incluso il terzo. Ho il sospetto che ci sia un modo per eliminare anche gli ultimi elementi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.