Гибридный код в скриптах оболочки. Совместное использование переменных

10

В этом ответе обсуждается, как запустить многострочный фрагмент Python из командной строки в терминале. Я заметил, что ответ прекрасно работает в сценариях оболочки, даже с вложенными отступами, что очень хорошо, например

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

печатает:

0 
hello
hello
1
hello
hello
2
hello
hello

Тем не менее, я испытываю трудности с разделением переменных между сценарием оболочки и фрагментом Python.

  1. Как я могу собрать выходные данные индекса Python в скрипте bash? (например, в переменной, такой как $output).

  2. Как я могу передать переменную bash (например $some_text) в скрипт Python?

Амелио Васкес-Рейна
источник
1
Вы можете сделать python - <<EOFвместо этого.
JFTR IMO, это плохой стиль, и вы должны попытаться избежать этого
Ульрих Дангел
1
Почему тег / zsh?
Стефан Шазелас

Ответы:

12

Получение переменной в Python

Поскольку (когда EOFмаркер не указан в кавычках), подстановка переменной происходит до того, как текст передается из heredoc в pythonстандартный ввод в России, вы можете бросить переменную прямо в сценарий.

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

Если бы some_textбыло test, Python увидел бы some_text = "test". Однако обратите внимание, что это можно рассматривать как уязвимость внедрения кода. Если бы some_textбыл "; import os; os.system("evil-command"); x = ", например, Python увидел бы:

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

и выполнить эту злую команду.

Если вы хотите иметь возможность вставить свой код Python прямо в скрипт без каких-либо изменений, вы можете экспортировать свою переменную.

export some_text

и использовать, os.environчтобы получить его.

some_text = os.environ['some_text']

Это гораздо разумнее / безопаснее.


Получение вывода из Python

Вы можете использовать подстановку команд для сбора выходных данных скрипта.

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

(обратите внимание, что все завершающие символы новой строки удалены)

Стефан Шазелас
источник
Это выглядит великолепно. Когда вы сказали "You could also pass the variable to the script as an argument", как бы вы это сделали, учитывая, что скрипт Python встроен в скрипт оболочки?
Амелио Васкес-Рейна
Извините, я забыл об этом на секунду. Я удалил это решение и добавил лучшее решение.
Будьте осторожны, как вы называете маркер heredoc. Когда оно не заключено в кавычки, как в вашем примере, расширение оболочки будет происходить внутри строки heredoc. Например, если ваш код на python состоит из just print '$SHELL', результат будет /bin/bashили то, чем будет ваша оболочка. Если вы измените первую строку на python - <<'END', будет вывод $SHELL. Если вы копируете и вставляете существующий код для встраивания в bash-скрипт, вам почти наверняка не нужны замены оболочки - вы хотите, чтобы код работал так же, как и в случае, если он не встроен в bash-скрипт, верно?
Крис Джонсон
8

Проблема с вашим подходом заключается в том, что встроенный скрипт Python больше не имеет доступа к исходному стандартному стандартному входу (поскольку его стандартный ... сам по себе).

Если это проблема, вы можете написать:

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

Или, если скрипт Python может содержать одинарные кавычки:

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

Или:

python <(cat << 'EOF'
import sys;
for r in range(3):
  print r
  for a in range(2):
    print "hello"
EOF
)
Стефан Шазелас
источник
6

Используйте тире в качестве имени файла:

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

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

Я не знаю, sys.argv[1:]является ли правильный способ сделать это в Python. Для -e / -c вы можете указать конец аргумента с помощью -:

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

Захват вывода и перенаправление STDERR:

x=$(ruby <<'END' 2> /dev/null
puts "a"
abort "b"
END
)
LRI
источник
2

1) Вы можете записать переменные в файл на python, а затем получить его в своем скрипте bash.

2) Поскольку ваше слово (EOF) не заключено в кавычки, все строки здесь-документа подвергаются расширению параметров. Вы можете использовать это для передачи содержимого в скрипт Python.

Ролан Смит
источник