Parentesi in aritmetica expr: 3 * (2 + 1)


60

expr non sembra che la parentesi (usata in matematica per esplicita priorità dell'operatore):

expr 3 * (2 + 1)
bash: syntax error near unexpected token `('

Come esprimere la priorità dell'operatore in bash?

Risposte:


40

Un altro modo di usare letbash builtin:

$ let a="3 * (2 + 1)"
$ printf '%s\n' "$a"
9

Nota

Come ha sottolineato @ Stéphane Chazelas , bashdovresti usare ((...))l'aritmetica expro la letleggibilità.

Per la portabilità, utilizzare $((...))come risposta @Bernhard .


1
+1 Ancora più leggibile! Ho pubblicato la mia domanda + risposta pensando solo che sarebbe stato utile per i miei colleghi utenti Linux, ma ora sto beneficiando molto delle altre risposte :-)
Nicolas Raoul,

3
Non c'è motivo di usare let. Non è più standard o portatile di (( a = 3 * (2 + 1) ))(entrambi provengono kshe sono disponibili solo in ksh, bash e zsh) ed è meno leggibile o facile da citare. Usa a=$((3 * (2 + 1)))per essere portatile.
Stéphane Chazelas,

2
Non sto dicendo che è sbagliato, sto solo dicendo che non dovrebbe essere usato in quanto ci sono alternative migliori (una per la leggibilità ((a = 3 * (2 + 1) )), una per la portabilità a=$((3 * (2 + 1)))), quindi non è una nota contro di te o la tua risposta ma contro che è la risposta selezionata e capocannoniere.
Stéphane Chazelas,

@ StéphaneChazelas: aggiornata la mia risposta!
cuonglm,

Ho sempre usato a=1 $[a+2]o a=1 b=2 $[a+b]. È la loro ragione per evitare quella sintassi?
Gordon,

74

È invece possibile utilizzare l'espansione aritmetica.

echo "$(( 3 * ( 2 + 1 ) ))"
9

Secondo me, sembra un po 'più bello dell'uso expr.

A partire dal man bash

Espansione aritmetica L'espansione aritmetica consente la valutazione di un'espressione aritmetica e la sostituzione del risultato. Il formato per l'espansione aritmetica è:

         $((expression))

L'espressione viene trattata come se fosse racchiusa tra virgolette doppie, ma una virgoletta doppia tra parentesi non viene trattata in modo speciale. Tutti i token nell'espressione subiscono l'espansione dei parametri, l'espansione della stringa, la sostituzione dei comandi e la rimozione delle virgolette. Le espansioni aritmetiche possono essere nidificate.

La valutazione viene eseguita secondo le regole elencate di seguito in VALUTAZIONE ARITMETICA. Se l'espressione non è valida, bash stampa un messaggio che indica l'errore e non si verifica alcuna sostituzione.


1
A parte la leggibilità, non richiede neppure un ulteriore processo per eseguire l'aritmetica; è gestito dalla shell stessa.
Chepner,

Si noti che nelle shell POSIX è soggetto alla suddivisione delle parole, quindi è una buona abitudine citarlo in contesti di elenchi.
Stéphane Chazelas,

Quando provo questo nella shell bash ottengo "Nome variabile illegale".
lordhog

40

Non c'è motivo di usare l' expraritmetica nelle conchiglie moderne.

POSIX definisce l' $((...))operatore di espansione. Quindi puoi usarlo in tutte le shell compatibili con POSIX (le più shmoderne di tipo Unix, dash, bash, yash, mksh, zsh, posh, ksh ...).

a=$(( 3 * (2 + 1) ))
a=$((3*(2+1)))

kshha anche introdotto un letbuiltin a cui viene passato lo stesso tipo di espressione aritmetica, che non si espande in qualcosa ma restituisce uno stato di uscita in base al fatto che l'espressione si risolva in 0 oppure no, come in expr:

if let 'a = 3 * (2 + 1)'; then
  echo "$a is non-zero"
fi

Tuttavia, poiché la citazione la rende scomoda e poco leggibile (non nella stessa misura exprovviamente), ha kshanche introdotto una ((...))forma alternativa:

if (( a = 3 * (2 + 1) )) && (( 3 > 1 )); then
  echo "$a is non-zero and 3 > 1"
fi
((a+=2))

che è molto più leggibile e dovrebbe essere usato invece.

lete ((...))sono disponibili solo in ksh, zshe bash. La $((...))sintassi dovrebbe essere preferita se è necessaria la portabilità ad altre shell, exprè necessaria solo per shell tipo Bourne pre-POSIX (in genere la shell Bourne o le prime versioni della shell Almquist).

Sul fronte non Bourne, ci sono alcune conchiglie con operatore aritmetico incorporato:

  • csh/ tcsh(in realtà la prima shell Unix con valutazione aritmetica integrata):

    @ a = 3 * (2 + 1)
  • akanga(basato su rc)

    a = $:'3 * (2 + 1)'
  • come nota storica, la versione originale della shell Almquist, pubblicata su usenet nel 1989, aveva un exprbuiltin (effettivamente unito a test), ma è stata rimossa in seguito.


Ogni giorno imparo qualcosa di nuovo da te, Stéphane. Apprezzo molto la tua conoscenza della shell POSIX!
MattBianco,

Che ne dici : $((a = a*2))?
Arthur2e5,

Cosa succede se ho un virgola mobile? La mia espressione è a = $ ((-14 + 0,2 * (1 + 2 + 3))). Il token di errore è ".2 * (1 + 2 + 3)"
Blaise,

@ Blaise, allora avresti bisogno di una shell che supporti i punti mobili $((...))come zsh, ksh93 o yash.
Stéphane Chazelas,

16

exprè un comando esterno, non è una sintassi speciale della shell. Pertanto, se si desidera exprvisualizzare i caratteri speciali della shell, è necessario proteggerli dall'analisi della shell citandoli. Inoltre, exprogni numero e operatore deve essere passato come parametro separato. Così:

expr 3 \* \( 2 + 1 \)

A meno che tu non stia lavorando su un sistema unix antico degli anni '70 o '80, ci sono pochissime ragioni per usarlo expr. Ai vecchi tempi, le shell non avevano un modo integrato per eseguire l'aritmetica e invece si doveva chiamare l' exprutilità. Tutte le shell POSIX hanno un'aritmetica integrata tramite la sintassi di espansione aritmetica .

echo "$((3 * (2 + 1)))"

Il costrutto si $((…))espande al risultato dell'espressione aritmetica (scritta in decimale). Bash, come la maggior parte delle shell, supporta solo l'aritmetica intera modulo 2 64 (o modulo 2 32 per le versioni precedenti di bash e alcune altre shell su macchine a 32 bit).

Bash offre un'ulteriore sintassi di convenienza quando si desidera eseguire compiti o verificare se un'espressione è 0 ma non ci si preoccupa del risultato. Questo costrutto esiste anche in ksh e zsh ma non in sh.

((x = 3 * (2+1)))
echo "$x"
if ((x > 3)); then 

Oltre all'aritmetica dei numeri interi, exproffre alcune funzioni di manipolazione delle stringhe. Anche questi sono riassunti dalle caratteristiche delle shell POSIX, tranne uno: expr STRING : REGEXPverifica se la stringa corrisponde al regexp specificato. Una shell POSIX non può farlo senza strumenti esterni, ma bash può con [[ STRING =~ REGEXP ]](con una diversa sintassi regexpexprè uno strumento classico e usa BRE, bash usa ERE).

A meno che tu non stia mantenendo script eseguiti su sistemi di 20 anni, non devi sapere che siano exprmai esistiti. Usa l'aritmetica della shell.


expr foo : '\(.\)'fa anche l'estrazione del testo. bash's BASH_REMATCHRaggiunge qualcosa di simile. Fa anche il confronto delle stringhe, cosa che POSIX [non fa (sebbene si possano immaginare modi per usarlo sort).
Stéphane Chazelas,

sottolineatura come segnaposto di sintassi --- sei uno Schemer, @Giles? :]
RubyTuesdayDONO il

1
@RubyTuesdayDONO Non ho usato un trattino basso qui. Stai leggendo male U + 2026 ELLIPSIS ORIZZONTALE? In tal caso, prova a utilizzare un carattere più grande.
Gilles 'SO- smetti di essere malvagio' il

@Giles - okay, sì, sembra solo un trattino basso a causa della mia dimensione del carattere. per me "Schemer" è un complemento, e non è come se l'ellissi contro il carattere di sottolineatura cambiasse comunque il significato ... non c'è bisogno di rimuginarci sopra: /
RubyTuesdayDONO

12

Usa le parentesi tra virgolette:

expr 3 '*' '(' 2 '+' 1 ')'
9

Le virgolette impediscono a bash di interpretare la parentesi come sintassi di bash.


2
Ciò che Nicolas illustra ma non spiega è che i token sulla exprriga di comando devono essere separati da spazi; così; per esempio, expr 3 "*" "(2" "+" "1)" non funzionerà . (Inoltre, probabilmente non è necessario citare il +.)
G-Man dice 'Reinstate Monica'

Le parentesi non sono parole chiave come whilee [[sono sintassi. Se fossero parole chiave, non verrebbero interpretate come tali negli argomenti dei comandi. Hai bisogno di virgolette in modo che bash non le analizzi ma veda invece una stringa letterale.
Gilles 'SO- smetti di essere malvagio' il

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.