Déclaration variable en parallèle sh -c …

J'ai essayé de traiter la sortie de find avec parallel , qui invoquait à son tour un shell (certaines substitutions textuelles étaient nécessaires). J'ai observé un comportement étrange, que je ne peux pas vraiment m'expliquer.

Dans chaque directory il y a un tas de files, appelez-les file1.xtc , file2.xtc . Certains d'entre eux ont des noms tels que file1.part0002.xtc , etc. Si le file passé à partir de find avait le nom *.part000x.* , Je dois supprimer le *.part000x.* , De sorte que la command résultante soit comme

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

J'ai utilisé find et parallel à cet effet mais les substitutions de parallel (en particulier le bit {.} ) Ne suffisent pas (elles suppriment l'extension .xtc , laissant le .part0001 seul), donc voici une command que je vérifie ma sortie:

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

Si j'utilise la command ci-dessus, déclarant d'abord le name et lui assignant une string vide (ou toute autre chose d'ailleurs), le résultat est

 file1.part0001 file1.tpr 

au besoin (ce sont les noms que je dois utiliser pour ma command). Si, cependant, je cours ce

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

le résultat est:

 file1.part0001 .tpr 

ou il se comporte comme si $name n'existait pas.

Donc, mes questions sont les suivantes:

Quelle est la raison de ce comportement?

Quel serait le moyen préféré de le traiter?

La première question est plus importante ici, car la méthode que j'ai utilisée ci-dessus est une solution de contournement qui, bien que pas jolie, fonctionne. Ce n'est pas la première fois que je devais faire une substitution textuelle comme ça et ce comportement continue à me déconcerter.

Sortie de sh --version

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

sortie d'une version plus récente de bash que j'ai installé et utilisé à la place de sh dans la command ci-dessus (au même effet) ( /usr/local/bin/bash --version )

 GNU bash, version 4.2.0(1)-release (i386-apple-darwin11.4.2) 

Votre problème n'a rien à voir avec bash. En fait, puisque vous dites en parallel d'exécuter sh , vous ne pouvez même pas utiliser bash .

Le problème est que le parallèle n'est pas vraiment un rlocation de rlocation pour xargs, comme l'indique sa documentation. Au lieu de cela, il accumule ses arguments dans une seule string (avec des espaces entre eux), puis interprète cela comme une série de commands. Donc, dans votre cas, vous avez:

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

qui est interprété comme

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

Comme il s'agit de deux commands distinctes et que le premier s'exécute dans un sous-shell ( sh -c ), $name n'est pas défini dans le second.

Maintenant, vous pouvez append assez bien n'importe quoi au début de la string, comme true :

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

Cela sera interprété comme:

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

Dans ce cas, l'appel à sh est essentiellement un jet-away; alors le name est défini dans l'environnement maintenu par le parallel et finalement l' echo est appelé avec l'set de name .

Il semblerait donc que la solution la plus simple est simplement de se débarrasser de l'appel inutile à sh :

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

Note: Basé sur un indice donné par @StephaneChazelas, j'ai supprimé les guillemets autour de {.} Et les ai ajoutés autour de ${name%.*}.ptr . Parallèle fait sa propre citation de ses propres substitutions, qui interfère de façon étrange avec des citations explicites. Cependant, il n'ajoute pas de citation aux substitutions de coquille, qui devraient être citées s'il y a une possibilité de substitution de mots.

Une autre option, si vous voulez vraiment utiliser un sous-shell pour une raison quelconque (ou un sous-shell particulier), serait d'utiliser l'option -q :

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

Note: Comme ci-dessus, j'ai ajusté les guillemets. Dans ce cas, l'explicite -q supprime la citation des substitutions, vous devez donc les citer explicitement. Cependant, il s'agit d'une citation textuelle, qui est less précise que la citation de coquille; si la substitution inclut un caractère de guillemet double, ce caractère ne sera pas échappé, donc il fermera les guillemets explicites, brisera la command line et introduira effectivement une vulnérabilité d'injection de command (vous obtiendrez d'autres problèmes pour les noms de files contenant $ , , ou \ caractères). Pour cela, entre autres raisons, l'option -q est déconseillée.