L'espansione con * .txt nella shell non funziona se non esiste alcun file .txt


10

Stavo giocando con l'espansione e ho notato un comportamento peculiare. Ho provato a fare:

echo ./*.txt

E non avevo alcun file .txt nella mia directory corrente. L'output che ho ottenuto è stato:

./*.txt

Sono solo curioso: perché l'ho preso? Mi aspettavo di non ottenere alcun risultato.

PS: quando avevo un .txtfile, l'espansione era interpretata correttamente. In altre parole, supponiamo che avessi un file smthn.txt, l'eco in realtà echeggiò current_directory/smthn.txt.

Risposte:


15

Adattandosi dalla pagina man della shell bash,

bash esegue la scansione di ogni parola per i caratteri *,? e [. Se viene visualizzato uno di questi caratteri, la parola viene considerata come un motivo e sostituita con un elenco in ordine alfabetico di nomi di file corrispondenti al motivo. Se non viene trovato alcun nome file corrispondente e l'opzione shell nullglob non è abilitata, la parola rimane invariata. Se l'opzione nullglob è impostata e non viene trovata alcuna corrispondenza, la parola viene rimossa.

In questo caso presumo che nullglob non sia abilitato, quindi la parola rimane invariata, quindi l'output visualizzato.


2
È possibile modificare il comportamento nel modo seguente: shopt -s nullglobprodurrà stringhe vuote per pattern senza eguali e shopt -u nullglob(impostazione standard) produrrà il pattern stesso.
PerlDuck,

13

Mi aspettavo di non ottenere alcun risultato.

Se nullglobfosse il valore predefinito, molti comandi si comporterebbero in modo inaspettato, perché è (forse sfortunatamente) comune che i comandi trattino il caso di argomenti zero nomefile in un modo qualitativamente diverso rispetto al caso di uno o più argomenti nomefile.

Supponiamo che tu abbia abilitato nullglob( shopt -s nullglob) e ti trovi in ​​una directory in cui nessun file corrisponde *.txt. Quindi *.txtsi espanderà davvero nel nulla - non in un campo vuoto, ma in nessun campo - come previsto. Ma ciò avrebbe questi risultati:

  • ls *.txtdovrebbe elencare tutti i file nella directory corrente (tranne i file nascosti), perché è ciò che lsfa quando non si passa alcun argomento sul nome del file.
  • cat *.txtleggerebbe dallo standard input , perché quando catnon ha argomenti per il nome file, è come se lo avessi eseguito cat -. Se in esecuzione in modo interattivo, rimane in attesa di input. Molti comandi si comportano in questo modo.
  • cp *.txt dest/fallirebbe con l'errore cp: missing destination file operand after 'dest/'. Questo non è un disastro, ma è confuso e abbastanza diverso dal silenzioso successo che è probabilmente desiderato.
  • file *.txte vari altri programmi senza un comportamento speciale per il caso di argomenti con zero nomefile, fallirebbero comunque con un messaggio di errore o di utilizzo quando nessuno viene passato.
  • Anche i casi che sembrano intuitivamente come dovrebbero funzionare spesso non lo farebbero. printf 'Got file: "%s"\n' *.txtstamperebbe Got file: ""invece di niente.
  • L'incapacità accidentale di citare le occorrenze di *, ?e [che non sono destinate ad essere espanse dalla shell produrrebbe più spesso risultati ovviamente sbagliati, ma in modi che potrebbero essere difficili da capire. Ad esempio, se nessun nome di file nella directory corrente è iniziato con gedit, allora apt list gedit*(dove apt list 'gedit*'era previsto) diventerebbe giusto apt listed elencherà tutti i pacchetti disponibili.

Quindi è bene non avere questo comportamento senza averlo richiesto. Probabilmente la situazione pratica più comune che in realtà è semplificata nullglobè for f in *.txt. Vedi anche questa domanda (a cui la risposta di Sergiy Kolodyazhnyy è collegata).

La domanda più difficile a cui rispondere è perché - failglobdove è un errore di espansione avere un glob che non corrisponde a nessun file - non è l'impostazione predefinita in bash. Credo che la risposta di Sergiy Kolodyazhnyy capisca la ragione anche senza affrontarla direttamente. Mantenere i globs in espansione senza produrre un errore di espansione è (forse purtroppo) il comportamento standardizzato, ed è anche un comportamento tradizionale, e quindi previsto,. Sebbene bash non tenti di essere completamente conforme a POSIX a meno che non venga invocato con il nome sho non venga passata l' --posixopzione, molte delle sue scelte progettuali anche quando non sono in modalità POSIX seguono POSIX direttamente. Hanno dovuto scegliere alcuni comportamenti e ci sono degli svantaggi associati all'andare contro le aspettative degli utenti.


Penso che questo sia l'aspetto meno storicamente influente della questione, quindi l'ho salvato per ultimo ... ma vale la pena ricordare che c'è qualcosa di un po 'concettualmente strano nel nullglobcomportamento.

nullgloball'inizio sembra elegante perché, sintatticamente , tratta il caso di zero file corrispondenti non diversamente dal caso di uno, due o qualsiasi altro numero. I comandi che eseguiamo, per i quali i glob si espandono in argomenti, non tendono a trattarli allo stesso modo, come descritto sopra. Ma sintatticamente questo sembra giusto, che penso sia la motivazione della tua domanda.

Eppure, c'è un'altra, più sottile incoerenza che nullglobnon affronta - che in realtà amplifica. Il caso di caratteri zero globbing ("caratteri jolly") viene trattato in modo profondamente diverso da quello di uno, due o qualsiasi altro numero. Ad esempio, con shopt -s nullglob, se ab?d?fnon corrisponde ad alcun file, viene rimosso; se ab?dnon corrisponde ad alcun file, viene rimosso; ma se abnon corrisponde a nessun file (ovvero se non esiste un file il cui nome è esattamente ab) non viene comunque rimosso. Certo, sarebbe un disastro se fosse rimosso, perché potrebbe non essere destinato a fare riferimento a un file esistente nella directory corrente; potrebbe anche non fare riferimento a un file. Ma questo elimina ancora ogni speranza di coerenza totale.

I tre comportamenti forniti da bash - il valore predefinito del trattamento dei glob che non corrispondono a nessun file come se non fossero globs e il loro passaggio non espanso, il comportamento che ti aspettavi di trattarli (se perdonerai questa strana svolta di frase) in quanto indica lo zero di tutti i file che corrispondono ( nullglob) e il comportamento sicuro di considerarli errori ( failglob): tutti rappresentano approcci diversi all'ambiguità inerente alla shell che non è in grado di sapere se una determinata parola è intesa come un nome del file. La shell esegue le sue espansioni senza conoscere il modo in cui i particolari comandi che chiami tratteranno i loro argomenti.

Questo è uno dei tanti casi di separazione delle preoccupazioni . Nei sistemi il cui design segue la filosofia Unix, ogni parte ha lo scopo di fare una cosa e farlo bene . La shell elabora il testo in comandi e argomenti e invoca quei comandi, molti dei quali sono esterni alla shell stessa. Questo tende ad essere molto più bello e versatile dei sistemi in cui i comandi esterni sono essi stessi responsabili dell'esecuzione di tali trasformazioni (come con i tradizionali processori di comandi in DOS e Windows). Ma ha i suoi lati negativi occasionali.


Apparentemente, bash-4.3.39 (2) non ha failglob. Quindi non può essere il default perché non è sempre stato supportato.
Ruslan,

6

Il motivo principale è perché si tratta di un comportamento standard specificato da POSIX , lo standard che copre il linguaggio dei comandi della shell e tra l'altro la corrispondenza dei modelli (shell come bash, dashshell - impostazione predefinita di Ubuntu /bin/she kshseguire questo standard). Dalla sezione 2.13.3 Pattern usati per l'espansione del nome file :

Se il motivo non corrisponde ad alcun nome file o percorso esistente, la stringa del motivo deve essere lasciata invariata.

Questo ovviamente ha un effetto collaterale: abbinare un nome file che può essere letteralmente *.txt. L' nullglobopzione in bashe zshpuò aiutare: se tale opzione è abilitata tramite shopt -s nullglob(e non è abilitata per impostazione predefinita che si applica a questa domanda), allora globstar verrà espanso in stringa vuota quando non viene trovato alcun nome file corrispondente. ksh93ha un proprio meccanismo di corrispondenza dei modelli avanzato che ottiene lo stesso effetto~(N)*.txt

Vedi anche Perché nullglob non è predefinito?

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.