Come passare il carattere jolly '*' al parametro path del comando find tramite una variabile nello script?


9

Voglio usare findper trovare i file in un set di cartelle limitato da caratteri jolly, ma dove ci sono spazi nel nome del percorso.

Dalla riga di comando, questo è facile. I seguenti esempi funzionano tutti.

find  te*/my\ files/more   -print
find  te*/'my files'/more  -print
find  te*/my' 'files/more  -print

Questi troveranno i file in, per esempio, terminal/my files/moree tepid/my files/more.

Tuttavia, ho bisogno che questo faccia parte di una sceneggiatura; quello di cui ho bisogno è qualcosa del genere:

SEARCH='te*/my\ files/more'
find ${SEARCH} -print

Sfortunatamente, qualunque cosa faccia, non riesco a mescolare caratteri jolly e spazi in un findcomando all'interno di uno script. L'esempio sopra restituisce i seguenti errori (notare il raddoppio imprevisto della barra rovesciata):

find: te*/my\\’: No such file or directory
find: files/more’: No such file or directory

Anche il tentativo di utilizzare le virgolette non riesce.

SEARCH="te*/'my files'/more"
find ${SEARCH} -print

Ciò restituisce i seguenti errori, avendo ignorato il significato delle virgolette:

find: te*/'my’: No such file or directory
find: ‘files'/more’: No such file or directory

Ecco un altro esempio.

SEARCH='te*/my files/more'
find ${SEARCH} -print

Come previsto:

find: te*/my’: No such file or directory
find: files/more’: No such file or directory

Ogni variazione che ho provato restituisce un errore.

Ho una soluzione alternativa, che è potenzialmente pericolosa perché restituisce troppe cartelle. Converto tutti gli spazi in un punto interrogativo (carattere jolly a carattere singolo) in questo modo:

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /?}       # Convert every space to a question mark.
find ${SEARCH} -print

Questo è l'equivalente di:

find te*/my?files/more -print

Ciò restituisce non solo le cartelle corrette, ma anche terse/myxfiles/moreciò che non dovrebbe.

Come posso ottenere quello che sto cercando di fare? Google non mi ha aiutato :(


@KasiyA Sto usando bash; devi usare qualcos'altro, come non ho mai visto prima quella costruzione. Il comando risulta SEARCH: command not foundcon l' find -printesecuzione del comando .
Paddy Landau,

Uno scatto al buio, ma per quanto riguarda la citazione? find "${SEARCH}" -print?
Alaa Ali,

@AlaaAli No, non funziona, perché la quotazione impedisce a Bash di utilizzare i caratteri jolly. Cercherà un percorso specifico con il nome (nel mio esempio) te*/'my files'/more.
Paddy Landau,

Risposte:


9

Lo stesso comando dovrebbe funzionare perfettamente in uno script:

#!/usr/bin/env bash
find  te*/my\ files/ -print

Se si devono avere come una variabile, diventa un po 'più complessa:

#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print

AVVERTIMENTO:

L'uso in evalquesto modo non è sicuro e può comportare l'esecuzione di codice arbitrario e potenzialmente dannoso se i nomi dei file possono contenere determinati caratteri. Vedi bash FAQ 48 per i dettagli.

È meglio passare il percorso come argomento:

#!/usr/bin/env bash
find "$@" -name "file*"

Un altro approccio è quello di evitare del findtutto e utilizzare le funzionalità e i globbing estesi di bash:

#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done

L' globstaropzione bash ti consente **di abbinare ricorsivamente:

globstar
      If set, the pattern ** used in a pathname expansion con
      text will match all files and zero or  more  directories
      and  subdirectories.  If the pattern is followed by a /,
      only directories and subdirectories match.

Per farlo agire al 100% come trova e include dotfile (file nascosti), usa

#!/usr/bin/env bash
shopt -s globstar
shopt -s dotglob
for file in te*/my\ files/**; do echo "$file"; done

Puoi anche echoli direttamente senza il ciclo:

echo te*/my\ files/**

2
Ho impostato questa come risposta a causa dei commenti utili che Terdon ha fatto (senza dimenticare i commenti utili degli altri). Ho usato l'abilità globbing di Bash sulla riga di comando per passare più percorsi al mio script, anziché lo script cercando di risolverlo. Funziona bene.
Paddy Landau,

2

Che ne dici di array?

$ tree Desktop/ Documents/
Desktop/
└── my folder
    └── more
        └── file
Documents/
└── my folder
    ├── folder
    └── more

5 directories, 1 file
$ SEARCH=(D*/my\ folder)
$ find "${SEARCH[@]}" 
Desktop/my folder
Desktop/my folder/more
Desktop/my folder/more/file
Documents/my folder
Documents/my folder/more
Documents/my folder/folder

(*)si espande in una matrice di qualsiasi cosa corrisponda al carattere jolly. E si "${SEARCH[@]}"espande in tutti gli elementi dell'array ([@] ), con ciascuno citato singolarmente.

In ritardo, mi rendo conto che trovare se stesso dovrebbe essere in grado di farlo. Qualcosa di simile a:

find . -path 'D*/my folder/more/'

Un'idea intelligente, ma purtroppo non funziona. Perché? Perché il percorso stesso è tenuto in una variabile; quindi, INPUTPATH='te*/my files/moree SEARCH=(${INPUTPATH}). Indipendentemente da come vari il modo in cui lo faccio, finisco comunque con un risultato non funzionale. Questo sembra impossibile!
Paddy Landau,

Questo è ovviamente vero, ma l'OP deve farlo in uno script. Ciò cambia le cose poiché l'espansione dei caratteri jolly diventa significativamente più complessa e questo non funziona.
terdon,

@PaddyLandau In tal caso, perché non puoi usare findil -pathfiltro? Utilizza caratteri jolly e sicuramente non ha bisogno di espansione.
muru,

@muru È interessante; Non lo sapevo -path. Negli ultimi 10 minuti, però, ho capito la risposta: usa eval! Sembra essere più semplice di -path.
Paddy Landau,

2
@terdon e muru e tutti: grazie. Ho sentito quello che hai detto tutti e mi sono reso conto che avrei dovuto fare in modo che la mia sceneggiatura facesse una cosa e lasciare che Bash Globbing passasse più file o percorsi alla sceneggiatura. Ho modificato la mia sceneggiatura così. Funziona bene e si adatta meglio alla filosofia Linux. Grazie ancora!
Paddy Landau,

0

Ho finalmente scoperto la risposta.

Aggiungi una barra rovesciata a tutti gli spazi:

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /\\ }

A questo punto, SEARCHcontiene te*/my\ files/more.

Quindi utilizzare eval.

eval find ${SEARCH} -print

È così semplice! L'uso evalignora l'interpretazione che ${SEARCH}proviene da una variabile.



@terdon Grazie per l'avvertimento. Torna al tavolo da disegno!
Paddy Landau,

Sì, è sorprendentemente complicato. Ho appena aggiornato la mia risposta con un altro approccio, perché non usare invece il globbing? Se il problema persiste, ti suggerisco di pubblicare una nuova domanda su Unix e Linux che spieghi qual è il tuo obiettivo finale e perché devi avere lo schema come variabile. È più probabile che questo tipo di cose ottenga una risposta migliore lì.
terdon,
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.