Código híbrido em scripts de shell. Compartilhando variáveis

10

Esta resposta discute como executar um trecho de código Python com várias linhas a partir da linha de comando em um terminal. Percebi que a resposta funciona muito bem em scripts shell, mesmo com indentação aninhada, o que é muito bom, por exemplo

#!/bin/bash
some_text="Hello world"
echo $some_text

cat <<EOF | python -
import sys;
for r in range(3):
  print r
  for a in range(2):
    print "hello"
EOF

impressões:

0 
hello
hello
1
hello
hello
2
hello
hello

No entanto, estou com dificuldade para compartilhar variáveis ​​entre o shell script e o snippet do Python.

  1. Como posso coletar a saída do subscrito python no script bash? (por exemplo, em uma variável como $output).

  2. Como posso passar uma variável bash (por exemplo $some_text) para o script Python?

Amelio Vazquez-Reina
fonte
1
Você pode fazer em seu python - <<EOFlugar.
jftr imo isso é ruim estilo e você deve tentar evitar que
Ulrich Dangel
1
Por que a tag / zsh?
Stéphane Chazelas

Respostas:

12

Obtendo uma variável para Python

Como (quando o EOFmarcador não está entre aspas) a substituição da variável ocorre antes que o texto seja passado da pythonentrada padrão do heredoc para a entrada padrão, você pode jogar a variável diretamente no script.

python - <<EOF
some_text = "$some_text"
EOF

Se some_textfosse test, python veria some_text = "test". Observe, no entanto, que isso pode ser visto como uma vulnerabilidade de injeção de código. Se some_textfosse "; import os; os.system("evil-command"); x = ", por exemplo, o python veria:

some_text = ""; import os; os.system("evil-command"); x = ""

e execute esse comando maligno.

Se você deseja inserir seu código Python em um script sem nenhuma modificação, pode exportar sua variável.

export some_text

e use os.environpara recuperá-lo.

some_text = os.environ['some_text']

Essa é uma abordagem muito mais saudável / segura.


Obtendo saída do Python

Você pode usar a substituição de comandos para coletar a saída do script.

output=$(
python - <<EOF
import sys;
for r in range(3):
  print r
  for a in range(2):
    print "hello"
EOF
)

(observe que todos os caracteres de nova linha à direita foram removidos)

Stéphane Chazelas
fonte
Isso parece ótimo. Quando você disse "You could also pass the variable to the script as an argument", como você faria isso, considerando que o script Python está incorporado no shell script?
Amelio Vazquez-Reina
Desculpe, eu esqueci disso por um segundo. Eu removi essa solução e adicionei uma solução melhor.
Tenha cuidado com o nome do marcador heredoc. Quando não está entre aspas, como no seu exemplo, a expansão do shell ocorrerá na cadeia heredoc. Por exemplo, se o seu código python consistisse apenas print '$SHELL', a saída seria /bin/bashou seja qual for o seu shell. Se você alterar a primeira linha para python - <<'END', a saída seria $SHELL. Se você está copiando e colando o código existente para incorporar em um script bash, quase certamente não deseja substituições de shell - você deseja que o código seja executado da mesma maneira que quando não está incorporado em um script bash, certo?
Chris Johnson
8

O problema com sua abordagem é que o script python incorporado não tem mais acesso ao stdin original (já que o stdin é ... ele próprio).

Se esse é um problema, você pode escrever:

python -c '
import sys;
for r in range(3):
  print r
  for a in range(2):
    print "hello"
'

Ou se o script python puder conter aspas simples:

python -c "$(cat << 'EOF'
import sys;
for r in range(3):
  print r
  for a in range(2):
    print "hello"
EOF
)"

Ou:

python <(cat << 'EOF'
import sys;
for r in range(3):
  print r
  for a in range(2):
    print "hello"
EOF
)
Stéphane Chazelas
fonte
6

Use um traço como o nome do arquivo:

ruby - a b <<'END'
puts ARGV.join(",")
END

python - a b <<'END'
import sys
print ",".join(sys.argv[1:])
END

Não sei se sys.argv[1:]é o caminho certo para fazer isso em Python. Para -e / -c, você pode especificar o final dos argumentos com -:

set -- -a -b -c
ruby -e 'puts ARGV.join(",")' -- "$@"
python -c 'import sys; print ",".join(sys.argv[2:])' -- "$@"

Capturando saída e redirecionando STDERR:

x=$(ruby <<'END' 2> /dev/null
puts "a"
abort "b"
END
)
Lri
fonte
2

1) Você pode escrever atribuições de variáveis ​​em um arquivo em python e depois originar isso em seu script bash.

2) Como sua palavra (EOF) não é citada, todas as linhas do documento aqui estão sujeitas à expansão de parâmetros. Você pode usar isso para passar coisas para o script python.

Roland Smith
fonte