dirs=( 123* )
set -- "${dirs[@]/%//deep/and deeper}"
mkdir -p "$@"
Eu não acho que isso precise de uma explicação ...
Usando um padrão de shell, como {abc,def}xyz
, posso usá-lo para criar arquivos (ou diretórios) que ainda não existem:
$ ls
$ touch {abc,def}xyz
$ mkdir 123{a,b,c}
$ ls
123a 123b 123c abcxyz defxyz
O que me intriga é como criar um subdiretório dentro de cada conjunto de pastas que corresponda a um padrão. Por exemplo, agora que tenho 123a
, 123b
e 123c
posso criar um subdiretório em cada um deles sem ter que recorrer a listá-los explicitamente?
$ mkdir {12*}/rsnapshot # Fails because 12* isn't a list
$ mkdir 12*/rnapshot # Fails because 12*/rsnapshot doesn't exist
$ mkdir 123{a,b,c}/rsnapshot # Succeeds but more complex than I'd like
O último exemplo funciona, mas requer que eu liste algumas partes de cada nome de diretório na subcláusula {...}
. Se eu tiver um grande número de diretórios, isso não é uma novidade.
Eu também tenho essa linha para trabalhar por nomes simples que não contêm espaços, mas não é nem óbvio nem elegante para uma solução generalizada:
echo -n 12* '' | xargs -r -d ' ' -I {} echo mkdir {}/rsnapshot
Existe um modelo de padrão em bash
que me permitirá criar arquivos ou subdiretórios em um grande conjunto de subdiretórios com nome semelhante sem um loop?
Apenas chame novamente argumentos de comando anteriores com !$
mkdir 123{a,b,c}
mkdir !$/rsnapshot
Você poderia usar o comando find para criar sub-diretórios dentro do nome do diretório padrão especificado, se você não for com loops .
find . -maxdepth 1 -type d -name "123*" -exec mkdir '{}/rsnapshot' \;
Não vejo nenhuma chance de obter uma solução que seja qualificada como "menos complexa que 123 {a, b, c} / rsnapshot". Mas talvez nossas opiniões como complexo isso é muito diferente ...
Mas posso oferecer algo "semelhante a um shell". Infelizmente, parece impossível (com bash
) configurar o caractere que separa os elementos resultantes da expansão do nome do caminho, assim ele fica um pouco mais, eh, complexo:
:> dirs=(12*/.) # the dot makes it match directories only
:> echo ${dirs[*]}
123a/. 123b/. 123c/.
:> IFS=,
:> eval echo mkdir -p "{${dirs[*]}}/rsnapshot"
mkdir -p 123a/./rsnapshot 123b/./rsnapshot 123c/./rsnapshot
Claro, isso funciona somente se nenhuma das combinações de expansão de nome do caminho contiver uma vírgula. Isso pode ser evitado com $GLOBIGNORE
.
printf
e xargs
Eu estou corrigido:
printf "%s/rsnapshot:> dirs=(12*/.) # the dot makes it match directories only
:> echo ${dirs[*]}
123a/. 123b/. 123c/.
:> IFS=,
:> eval echo mkdir -p "{${dirs[*]}}/rsnapshot"
mkdir -p 123a/./rsnapshot 123b/./rsnapshot 123c/./rsnapshot
" 12*/. | xargs -0 echo mkdir -p
Você pode fazer isso gerando uma matriz de diretórios existentes e expandindo a matriz com uma substituição que acrescenta "rsnapshot" a cada elemento:
$ dirs=(123*/) # Make an array with the currently-existing subdirs
$ echo "${dirs[@]}" # Check the contents before proceeding
123a/ 123b/ 123c/
$ echo mkdir "${dirs[@]/%/rsnapshot}" # echo to show what it'll do without doing it
mkdir 123a/rsnapshot 123b/rsnapshot 123c/rsnapshot
(Para usá-lo, remova o echo
s conforme necessário.)
Há um pouco de sutileza aqui: incluí um "/" no padrão que usei para criar o array; Isso significa que ele corresponderá apenas aos diretórios, e não aos arquivos que corresponderem ao padrão. Isso também significa que "/" é explicitamente incluído em cada elemento da matriz (como você pode ver quando eu echo
), então eu só preciso acrescentar "rsnapshot", não "/ rsnapshot".
A substituição final, "${dirs[@]/%/rsnapshot}"
, pode ser lida como: pegue cada elemento de dirs
( dirs[@]
) e substitua ( /
) o fim de string ( %
) por ( /
) a string "rsnapshot".
Com zsh
, você pode fazer:
l=(12*)
mkdir $^l/snapshot
O $^array
ativa algum tipo de expansões semelhantes a chaves para matrizes, como é feito implicitamente em rc
/ es
ou fish
.
Ou:
mkdir 12*(e:REPLY+=/snapshot:)
com rc
/ es
l=(12*)
mkdir $l/snapshot
com fish
:
set l 12*
mkdir $l/snapshot
Como parece (de outras respostas permitidas) que qualquer 123
dir é válido:
dirs=(123*/)
mkdir "${dirs[@]/%/rsnapshot}"
Como você está criando os diretórios, você pode estender isso para:
mkdir -p 123{a..c}{,/rsnapshot}
Isso criará os diretórios que você deseja, bem como os subdiretórios solicitados.
Mas suas perguntas não especificam: Somente os diretórios criados anteriormente .
Isso torna a seleção de diretórios difusa, na melhor das hipóteses.
Se não houver limite quanto a quais diretórios devem ser estendidos com um subdiretório, também podemos selecionar todos eles:
dirs=( */ )
mkdir "${dirs[@]/%/rsnapshot}"
Uma seleção mais limitada poderia ser feita com os nomes dos diretórios. Se os nomes começarem com alguns caracteres (123):
dirs=( 123*/ )
mkdir "${dirs[@]/%/rsnapshot}"
Se os diretórios forem criados antes da seleção: encontrar diretórios mais novos pode ser a solução:
sleep 5 # newest directory created is older than 5 seconds.
mkdir 123{a..d} # create newer directories.
find . -type d -newermt '4 seconds ago'
Em dois passos, o histórico da shell pode ser incluído:
$ mkdir -p 123{a..c}
$ mkdir -p !${,/rsnapshot}
O Eval é uma solução distinta, mas geralmente bastante perigosa.
O padrão de chaves é para expandir as repetições; Como você aponta, não é comparado com os arquivos existentes e, portanto, não é realmente um "padrão de nome de arquivo". Para criar subdiretórios em diretórios existentes, acho que é mais simples (e mais geral) fazer da maneira antiga:
mkdir 123{a,b,c}
for dir in 123*
do
mkdir $dir/rsnapshot
done
Isso não foi tão difícil, foi? Ou, se você é fã de xargs
, pode usar isso:
echo 123* | xargs -I {} mkdir {}/rsnapshot
Na minha opinião, a melhor solução é aquela que usa ferramentas comuns cujo uso você pode lembrar quando você quer apenas fazer o trabalho e seguir em frente.
Tags bash