Comportamento degli stessi comandi (sotto Bash) - eseguendo uno per uno in console vs come singolo script


1

In esecuzione su OS X 10.11.1, eseguo la seguente serie di comandi (uno per uno), nella console:

FILE="a b c.tiff"  # file in the current folder
VAR=$(mdls -name kMDItemContentCreationDate $FILE) # storing the creation time string  
TS=$(echo ${VAR[2]}; echo ${VAR[3]}  # saving the date and time only
echo $TS  

e l'espansione funziona magnificamente. L'output mostra:

16/01/2016 15:34:29

Tuttavia, quando li salvo in uno script ed eseguo, sembra che qualcosa durante la valutazione sia diverso.

Rendimenti di debug (con bash -x ):

FILE='a b c.tiff'
mdls -name kMDItemContentCreationDate a b c.tiff
VAR='a: could not find a.'
echo
echo
TS=
echo

, quindi vedo l'espansione comportarsi diversamente.

La mia preoccupazione è perché si verifica questa differenza e come devo correggere la mia sceneggiatura. Grazie.

Risposte:


2

Sono abbastanza sicuro che non hai eseguito quegli esatti comandi in una console, o avresti ottenuto lo stesso risultato. Ci sono due problemi seri qui e alcune cattive pratiche di scripting. Prima i problemi gravi:

  • Quando fai riferimento a una variabile (ad esempio $FILE) senza virgolette doppie, la shell la divide in "parole" e quindi espande i caratteri jolly prima di passarla al comando. In questo caso, ciò significa che a b c.tiffverrà suddiviso in "a", "b" e "c.tiff". Ecco perché ottieni "a: impossibile trovare a." errore.

    Soluzione: è necessario inserire riferimenti variabili tra virgolette, a meno che non si desideri specificamente la suddivisione delle parole e l'espansione dei caratteri jolly. (Ci sono alcuni casi in cui lasciare le virgolette doppie è sicuro, ma tenerne traccia è più problematico di quanto valga la pena. Prendi l'abitudine di usare le virgolette doppie.)

  • Quando si utilizza un compito simile VAR=$(somecommand), assegna la variabile come una stringa semplice, non come un array. Per memorizzarlo come un array, utilizzare le parentesi sul lato destro, come in VAR=( $(somecommand) ). Si noti che poiché $(somecommand)non è racchiuso tra virgolette doppie, sarà suddiviso in parole e con caratteri jolly, ma in questo caso vogliamo che si desideri dividere la parola (quindi ogni "parola" viene memorizzata in un elemento array separato) e il il formato di output è abbastanza prevedibile che l'espansione dei caratteri jolly non farà nulla di strano per rovinarci. Quindi questo è uno dei rari casi in cui lasciare le virgolette doppie va bene.

Con questi due fissi, la seconda riga diventa:

VAR=( $(mdls -name kMDItemContentCreationDate "$FILE") )

Ora, per alcune cose che sono cattive abitudini di scripting che in realtà non causano problemi qui:

  • Nell'assegnazione TS=$(echo ${VAR[2]}; echo ${VAR[3]})(nota: ho aggiunto la parentesi chiusa mancante), la sostituzione dei echocomandi e i comandi non stanno facendo nulla di utile. Quello che fa è prendere i valori degli elementi dell'array, word-split e jolly-espanderli (che qui non fa nulla), passarli come parametri ai echocomandi, prendere l'output di quei comandi e raccoglierlo in una variabile. È un sacco di lavoro per mettere insieme due corde. Usa semplicemente `TS =" $ {VAR [2]} $ {VAR [3]} ".

    A proposito, sta anche facendo qualcosa di un po 'strano: sta attaccando le corde insieme a una nuova linea tra di loro. Quando lo stampi con echo $TS, esso (di nuovo) viene diviso in parole, quindi ogni riga viene trattata come un argomento separato echo, che attacca gli spazi tra gli argomenti. Risultato netto: il echocomando sta effettivamente convertendo la nuova riga in uno spazio. Sarebbe molto più pulito renderlo uno spazio in primo luogo, quindi citarlo due volte quando lo usi, quindi la tua sceneggiatura non dipende da due bit di comportamento strano che si annullano a vicenda.

  • Infine, l'uso delle variabili in maiuscolo non è molto sicuro. Ci sono un certo numero di variabili in maiuscolo che hanno un significato speciale per la shell (e alcuni comandi), e se ne usi accidentalmente una puoi ottenere strani risultati. L'esempio classico sta assegnando qualcosa a PATH, a quel punto tutti i comandi improvvisamente non vengono riconosciuti. È difficile tenere traccia di tutte le variabili magiche, quindi basta usare variabili minuscole per le tue cose e sarai al sicuro.

Con tutto questo ripulito, ecco cosa ottengo per lo script:

file="a b c.tiff"  # file in the current folder
var=( $(mdls -name kMDItemContentCreationDate "$file") ) # storing the creation time string as an array
ts="${var[2]} ${var[3]}"  # saving the date and time only
echo "$ts"

Nota finale: in caso di dubbi, esegui la tua sceneggiatura attraverso shellcheck.net - ti segnalerà molti degli errori standard per principianti e ti farà risparmiare un sacco di tempo!


Grazie. Mi chiedo se incollare da Notes.app abbia avuto qualche tipo di effetto collaterale nel comportamento che stavo vedendo. Ad esempio, anche l'uso di virgolette doppie attorno a $ FILE non ha funzionato nello script per espandere il suo contenuto, ma piuttosto ha citato ad-literam (provato questo prima di pubblicare). Dovrò ricontrollare se fossero "virgolette intelligenti" o quant'altro. Ancora una volta ti ringrazierò per le chiare spiegazioni, nonché per i consigli sulle buone pratiche; inestimabile.
anziano anziano

@elderelder Sì, la maggior parte degli editor farà cose "utili" (come la sostituzione delle citazioni) che rovinano gli script. TextWrangler è una scelta molto migliore per gli script, perché fa praticamente quello che dici.
Gordon Davisson,

Apprezzo il suggerimento.
anziano anziano
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.