Bash vs ksh pipes

3

Estou com alguns problemas com meus scripts no ksh. FWIW o problema que eu não consigo superar é que quando eu uso uma estrutura como essa

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

Muitas vezes percebo que meus scripts não realizam o loop para cada linha canalizada para o tempo. Para testar isso, mudo a estrutura para

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

Isso prova que há muitas linhas na saída.

Além disso, eu adiciono uma linha extra imediatamente após o fazer, como

echo DEBUGGING: $VAR1  >&2

O que prova que o loop é executado apenas uma vez. Eu estou realmente perplexo.

Uma alternativa que nem sempre é viável é fazer

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

Isso funciona corretamente, mas além do fato de que eu odeio isso por estrutura, isso significa que você expande todos os dados de entrada na linha de comando (que tem limites rígidos)

Parece que o bash é melhor que o ksh em lidar com esse tipo de coisa. Em particular, parece que isso pode estar relacionado a chamadas de leitura falharem, mas não tentar novamente, se o loop demorar muito para ser executado.

No entanto, parece que o bash não tem um built-in "read", o que significa que muitos dos meus scripts precisarão ser reescritos. Eu frequentemente uso estruturas grandes como

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

O problema é que o bash usa / usr / bin / read, que, como esperado, joga fora o resultado de FINAL_ANSWER tão rápido quanto ele é obtido. A solução óbvia é substituir

| read FINAL_ANSWER

com

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

Então .... Algum script guru aqui capaz de lançar mais alguma luz sobre isso? Eu deliberadamente não postei meus scripts reais aqui porque eles são parte de uma solução sensível desenvolvida para um cliente, e porque eu não quero que o detalhe real dos scripts confunda o problema.

Eu uso o formato "while read" OFTEN. Geralmente funciona. Na verdade, eu nunca tive um problema com isso em 25 anos de scripts de shell. Agora estou com problemas. Muito frustrante. Perplexa.

Inicialmente, pensei que o tempo lido é apenas receber, ou passar adiante, a primeira linha de entrada. Mas então descobri uma situação em que, quando executo o script várias vezes, ele avança cada vez mais para a entrada. Especificamente eu tenho algo em linha isso

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

Acontece que em cada execução do script, ele executa dowork uma vez. Realmente não importa o que dowork é, desde que leve mais que alguns segundos. Algum tipo de tempo limite do tubo shell ocorre e o restante da entrada desaparece. O Google me diz que o dtksh pode resolver esse problema (aparentemente ele tentará ler / escrever ou algo assim, eu não li o suficiente)

Eu vejo que o dtksh existe em / usr / st / bin / dtksh

Quem é esse? Eu não gosto de usar shells que eu não conheço, mas pode valer a pena dividir pequenas porções de scripts em sub-scripts com / usr / dt / bin / dtksh como intérprete.

Algum conselho?

EDIT: Fornecendo um exemplo do porque eu não posso usar o bash como substituto para o ksh como interpretador:

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> #
    
por Johan 15.02.2013 / 16:08

1 resposta

4

Sua pergunta é um pouco desconexa. Vou responder o que parece ser a parte central, sobre a diferença entre ksh e bash que você observa.

Você encontrou o que é provavelmente a incompatibilidade # 1 entre o ksh e o bash quando se trata de scripts. ATT ksh (ambos ksh88 e ksh93) e zsh executam o último comando (mais à direita) em um pipeline no shell pai, enquanto outros shells (Bourne, ash, bash, pdksh, mksh) executam todos os comandos incluindo o último em um subshell .

Aqui está um programa de teste simples:

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

Em ATT ksh e zsh, a segunda atribuição a msg é executada no shell pai, portanto, o efeito fica visível após o pipeline. Em outros shells, essa atribuição é executada em uma subcamada, então a primeira atribuição permanece no pai.

Uma solução alternativa é executar o restante do script no pipeline. Este é um idioma comum para ler dados e fazer algum processamento posteriormente:

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

Você parece ter um bug do ksh . Eu recomendo atualizar para uma versão sem bugs. Se isso não for possível, tente a solução alternativa de Stephane Chazelas . Embora você possa tentar executar seus scripts no bash, não é (e não pretende ser) um substituto para o ksh; Há muitos recursos do ksh que o bash não possui (e vice-versa). Bash e ksh são compatíveis apenas no núcleo POSIX e em alguns outros recursos centrais (em particular, arrays, [[ … ]] e variáveis locais em funções declaradas por typeset ).

Você também pode tentar o zsh, que quando invocado como ksh se comporta de uma maneira um pouco mais próxima do ksh do que o bash. Você pode, no entanto, encontrar incompatibilidades.

    
por 16.02.2013 / 00:28