Em relação à expansão glob possivelmente excedendo um limite - sim e não. A concha já está funcionando e não vai parar. Mas se você passasse toda a matriz globbed como argumentos para um único comando, então sim, essa é uma possibilidade definida. A maneira portátil e robusta de lidar com isso envolve find
...
find . \! -name . -prune -name pattern -type f -exec cat {} + | ...
... que só irá cat
dos arquivos regulares no diretório atual com um nome que corresponda a pattern
, mas também invocará apenas cat
quantas vezes forem necessárias para evitar excedendo ARG_MAX
.
Na verdade, como você tem um GNU sed
, nós podemos quase fazer a coisa toda com apenas sed
em um script find
.
cd /path/to/xmls
find . \! -name . -prune -name \*.xml -type f -exec \
sed -sne'1F;$x;/\n*\( \)*<\/*double>/!d' \
-e '$s///gp;H' {} + | paste -d\0 - -
Eu pensei em outro jeito. Isso será muito rápido, mas isso depende absolutamente de haver exatamente 168 correspondências por arquivo, e só pode haver um .
dot nos nomes de arquivo.
( export LC_ALL=C; set '' - -
while [ "$#" -lt 168 ]; do set "$@$@"; done
shift "$((${#}-168))"
find . \! -name . -prune -name \*.xml -type f \
-exec grep -F '<double>' /dev/null {} + |
tr \<: '>>' | cut -d\> -f1,4 | paste -d\ "$@" |
sed 'h;s|./[^>]*>||g;x;s|\.x.*||;s|..||;G;s|\n| |'
)
Conforme solicitado, aqui está um pequeno resumo de como esse comando funciona:
-
( ... )
- Em primeiro lugar, todo o pequeno script é executado dentro de sua própria sub-camada porque existem algumas propriedades ambientais globais que estaremos alterando no decorrer de sua execução, e assim, quando o trabalho é executado, todas as as propriedades que alteramos serão restauradas para seus valores originais - quaisquer que fossem elas.
-
%código%
- Ao definir a localidade atual como
export LC_ALL=C; set '' - -
, podemos economizar muito esforço em nossos filtros. Em um código de idioma UTF-8, qualquer caractere pode ser representado por um ou vários bytes por peça, e qualquer caractere encontrado precisará ser selecionado de um grupo de muitos milhares de possíveis. Na localidade C, cada caractere é um único byte e há apenas 128 deles. Isso faz com que o char corresponda a um assunto muito mais rápido no geral. - A instrução
C
altera os parâmetros posicionais do shell. Fazendoset
conjuntosset '' - -
a$1
e
e$2
$3
a-
.
- Ao definir a localidade atual como
-
%código%
- Basicamente, o objetivo dessa declaração é obter uma matriz de 168 traços. Usaremos
while ... set "$@$@"; done; shift ...
depois para substituir conjuntos sequenciais de 167 novas linhas por espaços, preservando o 168º. A maneira mais simples de fazer isso é dar 168 referências de argumento parapaste
stdin e dizer para colar todas juntas.
- Basicamente, o objetivo dessa declaração é obter uma matriz de 168 traços. Usaremos
-
%código%
- O
-
bit foi discutido anteriormente, mas comfind ... -exec grep -F '<double>' /dev/null' ...
imprimimos apenas as linhas que podem ser comparadas com afind
ixed stringgrep
. Ao tornar o primeiro argumento-F
<double>
- que é um arquivo que pode nunca corresponder a nossa string - garantimos quegrep
esteja sempre pesquisando 2 ou mais argumentos de arquivo para cada chamada. Quando invocado com 2 ou mais arquivos de pesquisa nomeados,/dev/null
sempre imprimirá o nome do arquivo comogrep
na cabeça de cada linha de saída.
- O
-
%código%
- Aqui, traduzimos cada ocorrência na saída de
grep
dos caracteresfile_000.xml:
outr \<: '>>'
paragrep
. - Nesse ponto, uma amostra da linha correspondente será parecida com
:
.
- Aqui, traduzimos cada ocorrência na saída de
-
%código%
-
<
removerá de sua saída toda a sua entrada que não pode ser encontrada no primeiro ou no quarto campo dividido por>
chars. - Nesse ponto, uma amostra da linha correspondente será parecida com
./file_000.xml> >double>0.0000>/double>
.
-
-
%código%
- Já discutimos, mas aqui estamos
cut -d\> -f1,4
linhas de entrada em lotes de 168. - Nesse momento, 168 linhas correspondentes ocorrem juntas, como:
cut
- Já discutimos, mas aqui estamos
-
%código%
- Agora, os utilitários mais rápidos e menores já fizeram a maior parte do trabalho. Em um sistema multicore, eles provavelmente até fizeram isso concorrentemente. E esses utilitários - especialmente
>
e./file_000.xml>0.0000
são muito mais rápidos no que eles fazem do que qualquer tentativa de emulação que poderíamos fazer com utilitários de nível superior comopaste -d\ "$@"
ou, pior ainda,% código%. Mas eu levei isso até onde eu posso imaginar até agora, e eu tenho que chamarpaste
. - Primeiro, eu
./file_000.xml>0.000 .../file_000.xml>0.167
old uma cópia de cada linha de entrada, então eused 'h;s|./[^>]*>||g;x;s|\.xml.*||;s|..||;G;s|\n| |'
lobally remove todas as ocorrências do padrãocut
no espaço de padrão - portanto, toda ocorrência do nome do arquivo. Neste ponto, o espaço padrão depaste
é semelhante a:sed
- Em seguida, eu e
awk
changesed
espaços antigos e padrão e removo tudo deh
on - então tudo a partir do primeiro nome de arquivo na cópia salva da linha. Eu então retiro os dois primeiros caracteres - oug
também - e neste ponto o espaço padrão parece com./[^>]*>
. - Então tudo o que resta é colocá-los juntos. Eu
sed
et uma cópia de0.000 0.0001...0.167
old espaço acrescentado ao espaço padrão seguindo um caracterex
ewline, então euh
ubstitute o\.xml.*
ewline para um espaço. - Por fim, o espaço padrão é semelhante a
./
. E é isso quefile_000
grava na saída para cada arquivoG
passa parah
.
- Agora, os utilitários mais rápidos e menores já fizeram a maior parte do trabalho. Em um sistema multicore, eles provavelmente até fizeram isso concorrentemente. E esses utilitários - especialmente