Bash globbing e discussioni passate


8

Ho il seguente script bash semplificato

#!/bin/bash

files=("$@")

if [ "X$files" = "X" ]; then
  files=$HOME/print/*.pdf;
fi

for file in "${files[@]}"; do
  ls "$file";
done

Se passo argomenti (nomi file) come parametri, questo script stamperà i nomi file corretti. D'altra parte, se non passo argomenti, verrà stampato

/home/user/print/*.pdf: No such file or directory

Perché i nomi dei file non sono espansi in questo caso e come posso risolverli? Nota che io uso i costrutti files=("$@")e "${files[@]}"perché ho letto che deve essere preferito rispetto ai soliti "files = $ *".


Dov'è files=$*mai il solito ? Questo è chiaramente sbagliato .
Stéphane Chazelas,

Il solito è relativo, giusto. Intendevo un metodo che non utilizza array. Cosa faresti allora?
highsciguy,

Risposte:


10

Stai assegnando filescome variabile scalare invece che come variabile array .

In

 files=$HOME/print/*.pdf

Stai assegnando una stringa come /home/highsciguy/print/*.pdfalla $filesvariabile scalare (aka stringa).

Uso:

files=(~/print/*.pdf)

o

files=("$HOME"/print/*.pdf)

anziché. La shell espanderà quel modello globbing in un elenco di percorsi di file e assegnerà ciascuno di essi agli elementi $files dell'array .

L'espansione del glob viene effettuata al momento dell'incarico.

Non è necessario utilizzare funzioni sh non standard e si può usare il sistema shinvece di bashqui scrivendolo:

#!/bin/sh -

[ "$#" -gt 0 ] || set -- ~/print/*.pdf

for file do
  ls -d -- "$file"
done

setè assegnare l' "$@"array di parametri posizionali.

Un altro approccio potrebbe essere stato quello di memorizzare il modello globbing in una variabile scalare:

files=$HOME/print/*.pdf

E fai in modo che la shell espanda il glob nel momento in cui la $files variabile viene espansa.

IFS= # disable word splitting
for file in $files; do ...

Qui, poiché $filesnon è citato (cosa che normalmente non dovresti fare), la sua espansione è soggetta alla suddivisione delle parole (che qui abbiamo disabilitato) e alla generazione di globbing / nome file.

Quindi *.pdf verrà espanso all'elenco dei file corrispondenti. Tuttavia, se $HOMEcontenessero caratteri jolly, potrebbero essere espansi anche loro, motivo per cui è ancora preferibile utilizzare una variabile di matrice.


4

Potresti aver visto cose come files=$*e files=~/print/*.pdfnelle vecchie conchiglie senza array, e poi ls $files.

Una sostituzione variabile che non è racchiusa tra virgolette doppie interpreta il valore della variabile come un elenco separato da spazi bianchi di modelli jolly shell che vengono sostituiti da nomi di file corrispondenti, se presenti. Ad esempio, dopo files=~/print/*.pdf, si ls $filesespande in qualcosa di simile lsagli argomenti /home/highsciguy/print/bar.pdf, /home/highsciguy/print/foo.pdfecc. Nel caso files=$*, questa assegnazione concatena gli argomenti passati allo script con spazi tra di ls $filesloro e li divide.

Tutto ciò si interrompe se si hanno nomi di file contenenti spazi bianchi o caratteri globbing, motivo per cui non si dovrebbero fare le cose in questo modo. Utilizzare invece le matrici.

files=("$@")
if ((${#files[@]} == 0)); then
  files=("$HOME"/print/*.pdf)
fi

Nota che

  • Tutte le assegnazioni di array richiedono parentesi che racchiudono i valori di matrice: var=(…).
  • Per verificare se un array è vuoto, verificarne la lunghezza. "$files"è vuoto quando filesè un array il cui elemento dell'indice 0 non è impostato o una stringa vuota. Inoltre [ "X$foo" = "X" ]è un modo obsoleto per verificare se $fooè vuoto: tutte le shell moderne si implementano [ -n "$foo" ]correttamente. In bash, puoi usare [[ -n $foo ]].

Nelle shell che non supportano gli array, esiste infatti un array: i parametri posizionali della shell o la funzione corrente. Qui, non hai davvero bisogno filesdell'array, infatti sarebbe più facile usare i parametri posizionali.

#!/bin/sh
if [ "$#" -eq 0 ]; then
  set -- ~/print/*.pdf
fi
for file do 
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.