Código híbrido en scripts de shell. Compartir variables

10

Esta respuesta discute cómo ejecutar un fragmento de Python de varias líneas desde la línea de comandos en una terminal. Noté que la respuesta funciona muy bien dentro de los scripts de shell, incluso con sangría anidada, lo cual es muy bueno, por ejemplo

#!/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

huellas dactilares:

0 
hello
hello
1
hello
hello
2
hello
hello

Sin embargo, estoy teniendo dificultades para compartir variables entre el script de shell y el fragmento de Python.

  1. ¿Cómo puedo recopilar la salida del subíndice python en el script bash? (por ejemplo, en una variable como $output).

  2. ¿Cómo puedo pasar una variable bash (por ejemplo $some_text) al script Python?

Amelio Vazquez-Reina
fuente
1
Puedes hacer en su python - <<EOFlugar.
jftr imo este es un mal estilo y debes tratar de evitarlo
Ulrich Dangel
1
¿Por qué la etiqueta / zsh?
Stéphane Chazelas

Respuestas:

12

Obteniendo una variable a Python

Dado que (cuando EOFno se cita el marcador) la sustitución de la variable se produce antes de pasar el texto del heredoc a pythonla entrada estándar, puede lanzar la variable directamente en el script.

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

Si lo some_textfuera test, Python lo vería some_text = "test". Sin embargo, tenga en cuenta que puede verse como una vulnerabilidad de inyección de código. Si some_textfuera "; import os; os.system("evil-command"); x = ", por ejemplo, python vería:

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

y ejecuta ese malvado comando.

Si desea poder extraer su código Python directamente en un script sin ninguna modificación, puede exportar su variable.

export some_text

y úsalo os.environpara recuperarlo.

some_text = os.environ['some_text']

Ese es un enfoque mucho más sano / seguro.


Obteniendo salida de Python

Puede usar la sustitución de comandos para recopilar la salida del script.

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

(tenga en cuenta que se eliminan todos los caracteres de línea nueva finales)

Stéphane Chazelas
fuente
Esto se ve genial. Cuando dijiste "You could also pass the variable to the script as an argument", ¿cómo harías eso teniendo en cuenta que el script de Python está incrustado dentro del script de shell?
Amelio Vazquez-Reina
Lo siento, me olvidé de eso por un segundo. Quité esa solución y agregué una mejor solución.
Tenga cuidado de cómo nombra el marcador heredoc. Cuando no se cita como en su ejemplo, la expansión de shell ocurrirá dentro de la cadena heredoc. Por ejemplo, si su código de Python consistiera en solo print '$SHELL', el resultado sería /bin/basho lo que sea que sea ​​su shell. Si cambia la primera línea a python - <<'END', la salida sería $SHELL. Si está copiando y pegando el código existente para incrustarlo en un script bash, es casi seguro que no quiere sustituciones de shell: desea que el código se ejecute de la misma manera que cuando no está incrustado en un script bash, ¿verdad?
Chris Johnson
8

El problema con su enfoque es que el script de python incrustado ya no tiene acceso al stdin original (ya que su stdin es ... en sí mismo).

Si ese es un problema, puede escribir:

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

O si el script de Python puede contener comillas simples:

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

O:

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

Use un guión como nombre de archivo:

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

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

No sé si sys.argv[1:]es la forma correcta de hacer esto en Python. Para -e / -c puede especificar el final de los argumentos con -:

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

Capturando salida y redirigiendo STDERR:

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

1) Podría escribir asignaciones variables a un archivo en Python, y luego obtenerlo en su script bash.

2) Dado que su palabra (EOF) no se cita, todas las líneas del documento aquí están sujetas a la expansión de parámetros. Puede usar esto para pasar cosas al script de Python.

Roland Smith
fuente