Padrão de nome de arquivo para arquivos que ainda não existem

4

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?

    
por roaima 30.09.2017 / 15:17

8 respostas

3
dirs=( 123* )
set -- "${dirs[@]/%//deep/and deeper}"
mkdir -p "$@"

Eu não acho que isso precise de uma explicação ...

    
por 30.09.2017 / 21:27
4

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' \;
    
por 30.09.2017 / 16:01
3

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
    
por 30.09.2017 / 17:02
1

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".

    
por 01.10.2017 / 01:01
1

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
    
por 01.10.2017 / 12:17
1

Como parece (de outras respostas permitidas) que qualquer 123 dir é válido:

dirs=(123*/)
mkdir "${dirs[@]/%/rsnapshot}"
    
por 01.10.2017 / 21:19
1

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.

  1. 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}"
    
  2. 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}"
    
  3. 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'
    
  4. Em dois passos, o histórico da shell pode ser incluído:

    $ mkdir -p 123{a..c}
    $ mkdir -p !${,/rsnapshot}
    
  5. O Eval é uma solução distinta, mas geralmente bastante perigosa.

por 30.09.2017 / 21:15
0

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.

    
por 01.10.2017 / 03:09

Tags