Declaração de variáveis em paralelo sh-c…

4

Estou tentando processar a saída de find com parallel , que por sua vez invoca uma casca (algumas substituições textuais são necessárias). Eu observei um comportamento estranho, que não posso explicar para mim mesmo.

Em cada diretório há um monte de arquivos, chame-os de file1.xtc , file2.xtc . Alguns deles têm nomes como file1.part0002.xtc , etc. Se o arquivo passado de find tiver o nome *.part000x.* , preciso remover o *.part000x.* bit, de forma que o comando resultante seja cometa como

command -f file1.part0001.xtc -s file1.tpr 

Eu usei find e parallel para esse efeito, mas as substituições de parallel (em particular, o {.} bit) não são suficientes (elas removem a extensão .xtc , deixando a .part0001 sozinho), aqui está um comando que usei para verificar minha saída:

find 1st 2nd 3rd -name '*.xtc' -print0 | parallel -0 sh -c 'name=""; name="{.}"; echo {.} ${name%.*}.tpr'

Se eu usar o comando acima, declarando primeiro name e atribuindo uma string vazia a ele (ou qualquer outra coisa), o resultado é

file1.part0001 file1.tpr

conforme necessário (esses são os nomes que preciso usar para o meu comando). Se, no entanto, eu corro isso

find 1st 2nd 3rd -name '*.xtc' -print0 | parallel -0 sh -c 'name="{.}"; echo {.} ${name%.*}.tpr'

o resultado é:

file1.part0001 .tpr

ou se comporta como se $name não existisse.

Então as minhas perguntas são:

-Qual a razão para esse comportamento?

-Qual seria a maneira preferida de lidar com isso?

A primeira pergunta é mais importante aqui, pois o método que usei acima é uma solução alternativa, que, embora não seja bonita, funciona. Não é a primeira vez que eu preciso fazer uma substituição textual como essa e esse comportamento continua me desconcertando.

Saída de sh --version

GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin11)

saída de uma versão mais recente de bash que eu instalei e usei em vez de sh no comando acima (para o mesmo efeito) ( /usr/local/bin/bash --version )

GNU bash, version 4.2.0(1)-release (i386-apple-darwin11.4.2)
    
por Wojtek Rzepala 07.08.2013 / 19:24

1 resposta

5

Seu problema não tem nada a ver com o bash. Na verdade, como você está dizendo a parallel para executar sh , talvez você não esteja usando bash .

A questão é que o paralelismo não é realmente um substituto substituto para xargs, como indica sua documentação. Em vez disso, ele acumula seus argumentos em uma única string (com espaços entre eles) e, em seguida, interpreta isso como uma série de comandos. Então, no seu caso, você tem:

sh -c 'name="{.}"; echo {.} ${name%.*}.tpr'

que é interpretado como

sh -c 'name="{.}";
echo {.} ${name.*}.tpr

Como esses são dois comandos separados e o primeiro é executado em um subshell ( sh -c ), $name não é definido no segundo.

Agora, você pode adicionar qualquer coisa ao início da string, como true :

sh -c 'true; name="{.}"; echo {.} ${name%.*}.tpr'

Isso será interpretado como:

sh -c 'true'
name="{.}"
echo {.} ${name%.*}.tpr'

Nesse caso, a chamada para sh é essencialmente descartável; então name é definido no ambiente mantido por parallel e finalmente echo é chamado com name set.

Portanto, parece que a solução mais fácil é simplesmente livrar-se da chamada desnecessária para sh :

find 1st 2nd 3rd -name '*.xtc' -print0 |
parallel -0 'name={.}; echo {.} "${name%.*}.tpr"'

Nota: Com base em uma dica dada por @StephaneChazelas, removi as aspas em torno de {.} e as adicionei em torno de ${name%.*}.ptr . A parallel faz suas próprias citações de suas próprias substituições, o que interfere de alguma forma estranha com aspas explícitas. No entanto, ele não adiciona citações às substituições de shell, que devem ser citadas se houver alguma possibilidade de a substituição ser dividida em palavras.

Outra opção, se você realmente quiser usar um subshell por algum motivo (ou um subshell em particular), seria usar a opção -q :

find 1st 2nd 3rd -name '*.xtc' -print0 |
parallel -0 -q sh -c 'name="{.}"; echo "{.}" "${name%.*}.tpr"'

Observação: Como acima, ajustei as cotações. Nesse caso, o -q explícito suprime a cotação das substituições, portanto, você deve citá-las explicitamente. No entanto, esta é uma citação textual, que é menos precisa do que a de shell; se a substituição incluir um caractere de aspas duplas, esse caractere não terá escape, por isso fechará as aspas explícitas, quebrando a linha de comando e introduzindo efetivamente uma vulnerabilidade de injeção de comando (você obterá outros problemas para nomes de arquivos contendo $ , ' ou \ caracteres). Por isso, entre outras razões, a opção -q é desencorajada.

    
por 07.08.2013 / 22:17