Repetição de entradas de diretório no bash e salvamento em um array

1

(Veja a atualização no pé da pergunta).

Esta é uma pergunta de acompanhamento para "Fazer cópias de diretório usando encontrar ".

Esta questão envolveu manipular um monte de diretórios. muito complicado para lidar com um único comando, então eu decidi ir com uma abordagem que salvou a lista de diretórios para um array bash, apesar das reservas sobre portabilidade. Parece que o shell POSIX padrão (o padrão shell Unix, como eu o entendo) não tem matrizes.

O makefile que estou usando aparece abaixo. Isso funciona, exceto pelo último degrau. Um resumo do que estou tentando fazer é seguir.

Como discutido na pergunta anterior, quero dar uma olhada no diretório x86-headers , e coletar em uma matriz bash, uma lista de seus subdiretórios de nível superior que contêm o arquivo C/populate.sh (mas também contém outros arquivos). Na minha configuração de teste, por exemplo, há apenas um diretório em x86-headers que contém o arquivo libc/C/populate.sh , a saber libc .

Eu então executo algumas operações nesses subdiretórios. A maioria importante é que eu faça uma cópia temporária de cada diretório que parece com libc.tmp_g4zlb . Ou seja, dirname seguido por 'tmp_' seguido por uma string aleatória de 5 dígitos.

Então, algumas perguntas:

1) Como discutido na pergunta anterior, estou dando voltas diretório 'x86-headers'. Aqui eu ainda estou usando o find. @Gilles indicou em sua resposta que esta não era ideal. Ele pode estar certo. Problemas com encontrar aqui:

a) Os valores retornados parecem com ./libc . Eu não quero um líder ./ .

b) O comando de localização que estou usando lista todos os diretórios. Eu só quero considere aqueles que contêm um arquivo com o caminho relativo de C/populate.sh .

A abordagem que Gilles estava usando poderia ser melhor, mas eu não entendo isto. Veja a meta gilles abaixo. Eu gostaria de obter a lista dos diretórios e salvá-los em um array.

2) A parte que estou tendo problemas é o último passo, a saber

echo "(progn (require 'parse-ffi) (ccl::parse-standard-ffi-files :$$i'.tmp_'$(RND)))" | \
/usr/lib/ccl-bootstrap/lx86cl -I /usr/lib/ccl-bootstrap/lx86cl.image; done \

O bit relevante é que estou tentando passar o valor temporário libc.tmp_g4zlb para ccl, que é um compilador Common Lisp. Sem o substituição, seria parecido com

echo "(progn (require 'parse-ffi) (ccl::parse-standard-ffi-files :libc.tmp_g4zlb))" | \
/usr/lib/ccl-bootstrap/lx86cl -I /usr/lib/ccl-bootstrap/lx86cl.image; done \

Isso funciona. A versão usando $$i acima não. O problema parece para ser o líder ./ . Sem isso, deveria funcionar. Para o registro, o erro que recebo é

? > Error: Too many arguments in call to #<Compiled-function CCL::PARSE-STANDARD-FFI-FILES #x488B8406>:
>        3 arguments provided, at most 2 accepted. 
> While executing: CCL::PARSE-STANDARD-FFI-FILES, in process listener(1).

Este não é, no entanto, o erro que recebo ao passar ./libc.tmp_g4zlb para o compilador. Esse erro parece

> Error: Filename "/home/faheem/test/foo/x86-headers/.\/libc.tmp_g4zlb/C/**/" contains illegal character #\/
> While executing: CCL::%SPLIT-DIR, in process listener(1).

Então é possível que haja algo mais acontecendo.

3) A abordagem geral parece razoável? Por favor fique a vontade para sugerir possíveis melhorias, mesmo que envolva uma completa estratégia diferente.

#!/usr/bin/make -f
# -*- makefile -*-

export SHELL=/bin/bash
export RND:=$(shell tr -cd a-z0-9 < /dev/urandom | head -c5)
export CCL_DEFAULT_DIRECTORY=$(CURDIR)

clean:
    rm -rf x86-headers/libc.tmp*

foo: clean
    PATH=$$PATH:$(CURDIR)/ffigen4/bin; \
    echo $$PATH; \
    export CURDIR=$(CURDIR); \
    echo $$CURDIR; \
    array=( $$(cd x86-headers && find . -mindepth 1 -maxdepth 1 -type d) ); \
    cd x86-headers && \
    for i in "$${array[@]}"; do \
    echo $$i; done; \
    for i in "$${array[@]}"; do \
    mkdir -p $$i."tmp_"$(RND)/C; done; \
    for i in "$${array[@]}"; do \
    cp -p $$i/C/populate.sh $$i".tmp_"$(RND)/C; done; \
    for i in "$${array[@]}"; do \
    cd $$i".tmp_"$(RND)/C; ./populate.sh; done; \
    for i in "$${array[@]}"; do \
    echo $$i'.tmp_'$(RND); done; \
    for i in "$${array[@]}"; do \
    echo "(progn (require 'parse-ffi) (ccl::parse-standard-ffi-files :$$i'.tmp_'$(RND)))" | \
    /usr/lib/ccl-bootstrap/lx86cl -I /usr/lib/ccl-bootstrap/lx86cl.image; done; \

gilles:
    cd x86-headers;
    for x in */C/populate.sh; do \
    echo -- "$${x%%/*}$$suffix"; done; \

ATUALIZAÇÃO: É possível que a pergunta (ou perguntas) tenha se perdido em todos os detalhes. Então, deixe-me tentar simplificar as coisas. Em sua resposta , escreveu Gilles

for x in */C/populate.sh; do
  mkdir -- "${x%%/*}$suffix"
  mkdir -- "${x%%/*}$suffix/C"
  cp -p -- "$x" "./${x%%/*}$suffix/C"
done

Como comentei a pergunta dele, x aqui corresponde a padrões do formulário */C/populate.sh . Além disso, ${x%%/*} corresponde à primeira parte da sequência, ou seja, o nome do diretório de nível superior. Agora, algo como

for x in */C/populate.sh; do                                                                                                                       
    myarr[] = "${x%%/*}"                                                                                                                    
done

criaria uma matriz contendo uma lista de diretórios de nível superior, que é o que eu quero. No entanto, não sei qual sintaxe usar. Eu preciso usar um contador que executa o loop, como i=0, 1,... para indexar myarr no LHS. Se eu tivesse um código de trabalho como esse, resolveria meu problema.

    
por Faheem Mitha 02.08.2012 / 12:39

1 resposta

2

Se você quiser acrescentar coisas a um array em um loop, você pode usar algo como:

for x in */C/populate.sh; do
  myarr=(${myarr[@]} "${x%%/*}")
done

Veja a documentação arrays que contém muitas coisas que você pode fazer com elas.

Como alternativa, você pode usar += para anexar a uma matriz (consulte aqui ) assim:

  myarr+=("${x%%/*}")

Alguns comentários:

As I commented on his question, x here matches patterns of the form */C/populate.sh.

Não foi assim que eu expliquei. */foo/bar é um padrão glob. Ele é expandido pelo shell para uma lista de todos os arquivos / diretórios que correspondem a esse padrão. (Tente echo */C/populate.sh , você verá todas as correspondências impressas). O loop for itera sobre esse conjunto de correspondências, usando $x como a variável de loop.

Also, ${x%%/*} matches the first part of the string, namely the top level directory name.

${x%%/*} não "corresponde" a nada. É uma função de manipulação de string que opera em $x . A partir dos documentos , ${var%%string} remove a correspondência mais longa de string do final de% código%. Nesse caso, ele remove tudo, desde o primeiro $var e "retorna" (expande para).

Então, para quebrar as três linhas de código acima, o que acontece é:

  • o shell gera uma lista de itens (arquivos ou diretórios) que correspondem ao glob / .
  • para cada um desses, o corpo do loop é executado com */C/populate.sh definido para esse item
  • $x se expande para a lista de cada item na matriz
  • ${myarr[@]} se expande para ${x%%/*} menos tudo, desde o primeiro $x em diante
  • / é então reconstruído com seu conteúdo antigo mais o novo item reduzido.
por 02.08.2012 / 22:03