Bash vs ksh pipes

Je suis coincé avec quelques problèmes avec mes scripts dans ksh. FWIW le problème que je suis incapable de surmonter est que lorsque j'utilise une structure comme celle-ci

command | while read VAR1 do many.commands using $VAR1 done 

J'obtiens souvent que mes scripts n'effectuent pas la boucle pour chaque ligne passée au time. Pour tester cela, je change la structure

 command > /tmp/tempfile cat -n /tmp/tempfile >&2 cat /tmp/tempfile | while read VAR1 etc 

Cela prouve qu'il y a beaucoup de lignes dans la sortie.

En outre, j'ajoute ensuite une ligne supplémentaire immédiatement après le faire, comme

 echo DEBUGGING: $VAR1 >&2 

Ce qui prouve que la boucle ne fonctionne qu'une seule fois. Je suis vraiment perplexe.

Une solution de contournement qui n'est pas toujours viable est de le faire

 for X in $(cat /tmp/tempfile ) do ... done 

Cela fonctionne alors correctement, mais outre le fait que je déteste cela pour la structure, cela signifie que vous étendez l'set des données d'input sur la command line (qui a des limites ssortingctes)

Il semble que bash est mieux que ksh à manipuler ce genre de chose. En particulier, il semble que cela puisse être lié au fait que les appels de lecture échouent mais ne recommencent pas si la boucle prend du time à s'exécuter.

Cependant, il semble que bash ne possède pas de "lecture" embeddede, ce qui signifie qu'une grande partie de mes scripts devront être réécrits. J'utilise souvent de grandes structures comme

 command1 | command2 | while read SOMEVAR; do awk -F: "... long awk program" | sed "long sed program" ; done | sort -u | tail -1 | read FINAL_ANSWER 

Le problème est que bash utilise / usr / bin / read qui, comme prévu, rejette le résultat de FINAL_ANSWER aussi vite qu'il l'obtient. La solution évidente consiste à replace

 | read FINAL_ANSWER 

avec

 > /tmp/final_answer && FINAL_ANSWER="$(cat /tmp/final_answer)" 

Alors … Les gourous de script ici peuvent-ils nous éclairer un peu plus? Je n'ai délibérément pas posté mes vrais scripts ici parce qu'ils font partie d'une solution sensible développée pour un client et parce que je ne veux pas que les détails réels des scripts confondent le problème.

J'utilise le format "while read" OFTEN. Cela fonctionne habituellement. En fait, je n'ai jamais eu de problème avec cela dans 25 ans de scripts shell. Maintenant j'ai des problèmes. Très frustrant. Embarrassant.

Au départ, je pensais que le moment lu ne recevait que la première ligne d'input. Mais ensuite, j'ai découvert une situation où, lorsque j'exécute le script encore et encore, il va de plus en plus loin dans l'input. Plus précisément, j'ai quelque chose en ligne

 command | while read NEXT_ONE DONEFLAG do if [ $DONEFLAG = "yes" ] then echo Already completed work for $NEXT_ONE else dowork $NEXT_ONE && set_flag $NEXT_ONE fi done 

Il s'avère qu'à chaque exécution du script, il exécute dowork une fois. Peu importe ce que dowork est, tant que cela prend plus de quelques secondes. Une sorte de timeout d'attente de la tubulure se produit et le rest de l'input disparaît. Google me dit que dtksh peut résoudre ce problème (Apparemment, il va réessayer le lire / écrire ou quelque chose, je n'ai pas lu assez)

Je vois que dtksh existe dans / usr / st / bin / dtksh

Qui est-ce? Je n'aime pas utiliser des shells que je ne connais pas, mais il pourrait être intéressant de split de petites parties de scripts en sous-scripts avec / usr / dt / bin / dtksh en tant qu'interprète.

Aucun conseil?

EDIT: fournissant un exemple de pourquoi je ne peux pas utiliser bash comme un rlocation de rlocation pour ksh comme interprète:

 sol10-primary> # cat test.sh #!/bin/ksh echo hello| read VAR1 echo $VAR1 sol10-primary> # ./test.sh hello sol10-primary> # sed 's/ksh/bash/' <test.sh >test2.sh sol10-primary> # chmod +x test2.sh sol10-primary> # ./test2.sh sol10-primary> # 

Votre question est un peu décousue. Je vais répondre à ce qui semble être la partie centrale, sur la différence entre ksh et bash que vous observez.

Vous avez rencontré ce qui est probablement l'incompatibilité # 1 entre ksh et bash quand il s'agit de scripts. ATT ksh (ksh88 et ksh93) et zsh exécutent la dernière command (la plus à droite) d'un pipeline dans le shell parent, alors que les autres shells (Bourne, ash, bash, pdksh, mksh) exécutent toutes les commands y compris la dernière dans un sous-shell .

Voici un programme de test simple:

 msg="a subshell" true | msg="the parent shell" echo "This shell runs the last command of a pipeline in $msg" 

Dans ATT ksh et zsh, la deuxième affectation à msg est exécutée dans le shell parent afin que l'effet soit visible après le pipeline. Dans d'autres shells, cette affectation est exécutée dans un sous-shell de sorte que la première affectation rest en place dans le parent.

Une solution de contournement consiste à exécuter le rest du script dans le pipeline. Ceci est un idiome commun pour lire des données et faire un peu de traitement par la suite:

 output_some_stuff | { var= while IFS= read -r line; do var=$(process "$line") done use "$var" } 

Vous semblez avoir rencontré un bug ksh . Je recommand de passer à une version non-buggy. Si ce n'est pas possible, essayez la solution de contournement de Stéphane Chazelas . Alors que vous pouvez essayer d'exécuter vos scripts dans bash, ce n'est pas (et ne prétend pas être) un rlocation de rlocation pour ksh; il y a beaucoup de fonctionnalités ksh que bash n'a pas (et vice versa). Bash et ksh ne sont compatibles que dans leur kernel POSIX et certaines autres fonctionnalités centrales (en particulier les arrays, [[ … ]] et les variables locales dans les fonctions déclarées par typeset ).

Vous pouvez aussi essayer zsh, qui, lorsqu'il est invoqué comme ksh se comporte d'une manière un peu plus proche de ksh que bash. Vous pouvez néanless rencontrer des incompatibilités.