Crie muitos subdiretórios de uma só vez

3

Suponha que eu tenha um diretório / , e ele contém muitos diretórios /mydir , /hisdir , /herdir , e cada um deles precisa ter uma estrutura semelhante.

Para cada diretório em / , é preciso haver um diretório doc e dentro dele um arquivo doc1.txt .

Pode-se ingenuamente supor que eles poderiam executar

mkdir */doc
touch */doc/doc1.txt

mas eles estariam errados, porque os curingas não funcionam assim.

Existe uma maneira de fazer isso sem apenas fazer a estrutura uma vez em um exemplo, então cp ing para os outros?

E, se não, existe uma maneira de fazer a solução acima sem sobrescrever quaisquer arquivos existentes (suponha que mydir já contenha a estrutura com alguns dados que eu quero manter)?

EDIT: Eu também gostaria de evitar o uso de um script, se possível.

    
por 2mac 04.08.2014 / 22:12

4 respostas

5

com zsh :

dirs=(*(/))
mkdir -- $^dirs/doc
touch -- $^dirs/doc/doc1.txt

(/) é um qualificador globbing , / significa selecionar apenas diretórios.

$^array (que lembra o rc do operador ^ ) é ativar um tipo de expansão do tipo chave no array, portanto $^array/foo é como {elt1,elt2,elt3}/doc (onde elt1 , elt2 , elt3 são os elementos da matriz).

Também se pode fazer:

mkdir -- *(/e:REPLY+=/doc:)
touch -- */doc(/e:REPLY+=/doc1.txt:)

Em que e é outro qualificador de globbing que executa determinado código no arquivo a ser selecionado.

Com rc / es / akanga :

dirs = */
mkdir -- $dirs^doc
touch -- $dirs^doc/doc1.txt

Isso está usando o operador ^ , que é como um operador de concatenação aprimorada.

rc não suporta qualificadores (que é um recurso somente zsh). */ expande para todos os diretórios e links simbólicos para diretórios , com / anexado.

com tcsh :

set dirs = */
mkdir -- $dirs:gs:/:/doc::q
touch -- $dirs:gs:/:/doc/doc1.txt::q

Os :x são modificadores de histórico que também podem ser aplicados a expansões variáveis. :gs é para substituto global. :q cita as palavras para evitar problemas com alguns caracteres.

Com zsh ou bash :

dirs=(*/)
mkdir -- "${dirs[@]/%/doc}"
touch -- "${dirs[@]/%/doc/doc1.txt}"

${var/pattern/replace} é o operador substituto em shells semelhantes a Korn. Com ${array[@]/pattern/replace} , é aplicado a cada elemento da matriz. % significa no final .

Várias considerações:

dirs=(*/) inclui diretórios e links simbólicos para diretórios (e não há como excluir links simbólicos diferentes de usar [ -L "$file" ] em um loop), enquanto dir=(*(/)) (extensão zsh) inclui diretórios ( dir=(*(-/)) para incluir links simbólicos para diretórios sem adicionar a barra final).

Eles excluem diretórios ocultos. Cada shell tem uma opção específica para incluir arquivos ocultos).

Se o diretório atual é gravável por outros, você potencialmente tem problemas de segurança. Como se poderia criar um symlink para fazer com que você crie dirs ou arquivos onde você não gostaria. Mesmo com soluções que não consideram links simbólicos, ainda há uma condição de corrida, pois é possível substituir um diretório por um link simbólico entre o dirs=(*/) e o mkdir... .

    
por 04.08.2014 / 22:21
8

No bash, use algo como estas linhas:

mkdir -p {mydir,hisdir,herdir}/doc
touch {mydir,hisdir,herdir}/doc/doc1.txt

A sintaxe {...} é chamada de "expansão de chave" e, diferentemente da expansão do nome do caminho , onde o nome do arquivo deve existir, os resultados gerados não precisam corresponder a nada já existente. E o -p significa criar todos os componentes aninhados do caminho conforme necessário - caso contrário, você receberá um erro, já que o mkdir tenta criar os diretórios "doc" finais antes dos pais.

(Confira os exemplos na página man bash; criar subdiretórios como este é exatamente o caso de uso comum.)

Se mydir, hisdor e herdir já existirem e você não quiser redigitá-los, a solução de Stéphane Chazelas é provavelmente a mais inteligente, mas a menos que você faça isso o tempo todo, a inteligência nem sempre é a melhor - eu posso nunca me lembro do material de expansão do array bash, e aposto que muitos administradores de sistemas júnior não o reconheceriam. Nesse caso, acho que recomendo um loop ou find , assim:

find . -maxdepth 1 -mindepth 1 -type d \
   -execdir mkdir {}/doc \; -execdir touch {}/doc/doc1.txt \;

mas, na verdade, o loop simples tem a virtude de ser direto - e não muito mais de digitação!

    
por 04.08.2014 / 23:43
0
for d in /*/
do  [ -d "$d" ] || break
    f=$d/docs/doc1.txt    
    mkdir -p -- "${f%/*}"
    touch -- "$f"
done

Acho que cumpre seus objetivos. Talvez eu esteja sentindo falta de algo.

Se você pudesse:

set -- /*/
[ -d "$1" ] && 
printf 'd=$%d
    mkdir -p -- "$d/docs" && 
    touch -- "$d/docs/doc1.txt"
' $(seq "$#") | sh -s -- "$@"

Ou:

set -- /*/
while [ -d "$1" ] 
do  mkdir -p -- "$1"/docs
    touch -- "$1"/docs/doc1.txt
shift ; done
    
por 04.08.2014 / 22:39
0

Ok, vamos curtir :

ls -1|xargs -i% bash -c 'mkdir %/doc;>>%/doc/doc1.txt'


 - Estamos em um diretório como seu diretório inicial .

  • ls -1 1 lista herdir etc, um por linha (não há mais nada)

  • xarg executa um shell com um argumento de script, substituindo o% pelo diretório

  • Para cada diretório:

    • bash executa um argumento de string como um script de shell, contendo dois comandos

      • mkdir %/doc criando o diretório
      • >>%/doc/doc1.txt criando o arquivo vazio 2


$ ls
$ mkdir mydir hisdir herdir
$ ls                       
herdir  hisdir  mydir
$ ls -1|xargs -i% bash -c 'mkdir %/doc;>>%/doc/doc1.txt'
$ find
.
./herdir
./herdir/doc
./herdir/doc/doc1.txt
./hisdir
./hisdir/doc
./hisdir/doc/doc1.txt
./mydir
./mydir/doc
./mydir/doc/doc1.txt


1) Liberdade artística; encontrar. -maxdepth 1 -tipo d

2) >>file cria um arquivo vazio aqui, semelhante ao touch. É bash speciffic, veja man bash, use a tecla / para procurar por" Redirecionamento de saída "

    
por 05.08.2014 / 06:13