Quindi fare qualcosa del genere in bash
e la maggior parte delle altre shell non funzionerà per creare più sottodirectory o file all'interno di sottodirectory...
mkdir */test
touch */hello.txt
Ovviamente ci sono molti modi per farlo, il mio preferito è usare find
quando possibile invece di usare un for
loop, principalmente per la leggibilità.
Ma la mia domanda è, perché quanto sopra non funziona?
Da quello che ho capito è perché il file/percorso di destinazione completo non esiste, ma sicuramente è una buona cosa se sto cercando di mkdir
o touch
. Sono sempre andato avanti e non l'ho mai veramente messo in discussione.
Ma qualcuno ha una spiegazione decente per questo che mi aiuterà a capirlo una volta per tutte?
Risposta accettata:
Il punto che potresti non notare – con cui molte persone hanno problemi,
specialmente se hanno esperienza con altri sistemi operativi prima di arrivare a *nix
– è che, in molti altri sistemi operativi, i caratteri jolly sul riga di comando sono normalmente passati al comando elaborare come meglio crede. Ad esempio, nel prompt dei comandi di Windows
rename *.jpeg *.jpg
Considerando che, in *nix, per semplificare il lavoro
del/i programmatore/i dei singoli comandi (es. mv
), i caratteri jolly
(se non tra virgolette o con caratteri di escape) vengono gestiti dalla shell,
in modo indipendente dall'interfaccia e dalla funzionalità
del comando a cui i caratteri jolly sono argomenti.
La maggior parte dei programmi che accettano nomi di file come argomenti della riga di comando si aspettano che tali file esistano
(sì, mkdir
e touch
sono eccezioni a questa regola,
così come mkfifo
, mknod
e, in misura parziale, cp
, ln
, mv
e rename
;
e probabilmente ce ne sono altri),
quindi non ha davvero senso che la shell espanda i caratteri jolly
in nomi di file che non esistono.
E per la shell (e, con questo, intendo ogni shell –
Bourne, bash, csh, fish, ksh, zsh, ecc...) gestire le eccezioni in modo diverso
sarebbe probabilmente una seccatura.
Detto questo, ci sono un paio di modi per ottenere un risultato come quello che desideri.
Se sai a cosa si espanderà il carattere jolly,
e non è lungo, puoi generarlo con l'espansione parentesi:
touch {red,orange,yellow,green,blue,indigo,violet}/rgb.txt
Una soluzione più generale:
sh -c 'for arg do mkdir -- "$arg"/test; done' -- *
Gilles mi ha aiutato a trovare un altro modo
per farlo in bash.
benbradley=(*)
mkdir "${benbradley[@]/%//test}"
Ovviamente benbradley
è solo un identificatore qui; puoi usare qualsiasi nome
(ad es. qualsiasi singola lettera).
Mi ci sono voluti un paio di tentativi per farlo bene, quindi lasciami scomporre:
identifier=value
crea una variabile (scalare) denominataidentifier
(se non esiste già) e assegna il valorevalue
ad esso.
Ad esempio,G=Man
.
Puoi fare riferimento a una variabile scalare con$identifier
;
ad esempio,$G
, o, più sicuro, come${identifier}
.
Ad esempio,$Gage
e$Gilla
potrebbe essere indefinito,
ma${G}age
èManage
e${G}illa
èManilla
.identifier=(value1 value2 … )
crea una variabile array denominataidentifier
(se non esiste già) e gli assegna i valori elencati.
Ad esempio,
spectrum=(red orange yellow green blue indigo violet)
o
all_text_files=(*.txt)
$name
farà riferimento al primo elemento (e quindi è abbastanza inutile).
Puoi fare riferimento a un elemento arbitrario come ${name[subscript]}
;
ad esempio, ${spectrum[0]}
è red
e ${spectrum[4]}
è blue
.
E puoi usare ${name[@]}
e ${name[*]}
per fare riferimento all'intero array.
Quindi, eccolo a pezzi:
" ${ benbradley[@] / % / /test } "
${parameter/pattern/string}
espande il${parameter}
e sostituisce la corrispondenza più lunga
dipattern
constring
.- Se
pattern
inizia con#
,
deve corrispondere all'inizio del valore espanso diparameter
.
Sepattern
inizia con%
,
deve corrispondere alla fine del valore espanso diparameter
.
In altre parole,
%(the-rest_of_the_pattern)
si comporta come
(the-rest_of_the_regex)$
(sì, sembra un po' indietro).
Quindi un pattern
questo è solo un %
è come un'espressione regolare che è solo un $
–
corrisponde alla fine della stringa di input (spazio di ricerca).
- E sto usando una
string
di/test
.
Quindi questo sostituisce la fine del parametro con/test
(ovvero, aggiunge/test
al parametro). - Un'altra cosa su
${parameter/pattern/string}
non è intuitivo è che sempre termina con un}
.
Non è necessario e non può , termina con un terzo/
.
Pertanto, un/
instring
non può essere interpretato come un delimitatore,
e quindi possiamo avere unastring
di/test
senza bisogno di sfuggire al/
. - Se
parameter
è una variabile array
con pedice@
o*
,
l'operazione di sostituzione viene applicata a turno a ciascun membro dell'array. - Quando fai riferimento a un array come
${name[@]}
(anziché${name[*]}
) e inserisci i risultati tra virgolette,
mantieni l'integrità degli elementi dell'array
(ovvero, conservi gli spazi e altri caratteri speciali in essi)
senza combinare gli elementi separati in una parola lunga.