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=valuecrea una variabile (scalare) denominataidentifier(se non esiste già) e assegna il valorevaluead esso.
Ad esempio,G=Man.
Puoi fare riferimento a una variabile scalare con$identifier;
ad esempio,$G, o, più sicuro, come${identifier}.
Ad esempio,$Gagee$Gillapotrebbe essere indefinito,
ma${G}ageèManagee${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
dipatternconstring.- Se
patterninizia con#,
deve corrispondere all'inizio del valore espanso diparameter.
Sepatterninizia 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
stringdi/test.
Quindi questo sostituisce la fine del parametro con/test(ovvero, aggiunge/testal 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/instringnon può essere interpretato come un delimitatore,
e quindi possiamo avere unastringdi/testsenza 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.