17

I have a bash script, f, that contains python code. That python code reads from standard input. I want to be able to call my bash script as follows:

f input.txt > output.txt

In the example above, the python code will read from input.txt and will write to output.txt.

I'm not sure how to do this. I know that if I wanted to just write to a file, then my bash script would look like this

#!/bin/bash
python << EOPYTHON > output.txt
#python code goes here
EOPYTHON

I tried changing the second line in the code above to the following, but without luck

python << EOPYTHON $*

I'm not sure how else to go about doing this. Any suggestions?

EDIT I'll give a more concrete example. Consider the following bash script, f

#!/bin/bash
python << EOPYTHON 
import sys
import fileinput
for i in fileinput.input():
    sys.stdout.write(i + '\n')
EOPYTHON

I want to run my code with the following command

f input.txt > output.txt

How do I change my bash script so that it uses "input.txt" as the input stream?

5
  • What's wrong with f < input.txt > output.txt? Commented Mar 25, 2014 at 9:24
  • 1
    "I have a bash script, f, that contains python code." Why? You may find that code is easier to maintain if you stick to one language per source file. Commented Mar 25, 2014 at 9:30
  • So I'm interpreting the problem as you want to know where to redirect python output to directly in the script (i.e. that the script was called redirecting stoudt to output.txt). My answer should solve that, but still a bit curious as to why you can't just print python to stdout and let bash handle the redirection from outside the script? Commented Mar 25, 2014 at 9:33
  • @Johnsyweb You've never made a wrapper for a script written in another language? Commented Mar 25, 2014 at 9:37
  • 1
    @BroSlow: Sure I have, but a wrapper can call out to an external file rather than include it. This separation of concerns promotes testability and maintainability. Commented Mar 25, 2014 at 9:45

6 Answers 6

33

As no one mentioned this, here is what author requested. The magic is to pass "-" as argument to cpython (instruction to read source code from stdin):

With output to file:

python - << EOF > out.txt
print("hello")
EOF

Execution sample:

# python - << EOF
> print("hello")
> EOF
hello

As data can't be passed via stdin anymore, here is another trick:

data=`cat input.txt`

python - <<EOF

data="""${data}"""
print(data)

EOF
Sign up to request clarification or add additional context in comments.

2 Comments

Why is the - argument needed? Seems to me python << EOF ... EOF works just as well, unless you need to pass arguments to the ....
@BigMcLargeHuge check your python --help command. In some distros it is default argument, in some not. Omitting it is just defaulting to it on supported versions or binaries, while using it explicitly guaranties the result.
12

Updated Answer

If you absolutely must run the way you ask, you could do something like this:

#!/bin/bash
python -c 'import os
for i in range(3):
   for j in range(3):
     print(i + j)
'  < "$1"

Original Answer

Save your python code in a file called script.py and change your script f to this:

#!/bin/bash
python script.py < "$1"

1 Comment

+1; using -c with a Python source-code string is indeed key: it enables passing stdin input to the Python code (as opposed to stdin input being interpreted as Python code, as happens with the <<EOPYTHON syntax); it also enables passing arguments to the Python code that the latter can access with sys.argv[1], … (after import sys).
0

You can just check it against the file descriptor list for the process, i.e. on a proc filesystem you can print the redirection location for stdout with

readlink /proc/$$/fd/1

For example

> cat test.sh
#!/bin/bash
readlink /proc/$$/fd/1
> ./test.sh
/dev/pts/3
> ./test.sh > out.txt
> cat out.txt 
/home/out.txt

Comments

0

-c option from @Mark Setchell's answer should work.

Here's an alternative where you embed bash in Python script:

#!/usr/bin/env python
import sys
import fileinput
from subprocess import call

# shell command before the python code
rc = call(r"""
some bash-specific commands here
...
""", shell=True, executable='/bin/bash')

for line in fileinput.input():
    sys.stdout.write(line) #NOTE: `line` already has a newline

# shell command after the python code
rc = call(r"""
some /bin/sh commands here
...
""", shell=True)

Comments

0

Use process substitution for passing the python code. This frees up regular io indirection which can be used as usual.

❯ seq 1 5 > input.txt
❯ python3 <input.txt <(<< EOS
import sys
print("Running script with args: ", sys.argv)
for line in sys.stdin:
  print("line is %s" % line, end='')
EOS
)
Running script with args:  ['/proc/self/fd/11']
line is 1
line is 2
line is 3
line is 4
line is 5

Comments

0

For the sake of completeness, you can also use bash process substitution to trick python into reading from the substituted file descriptor. An example I used recently:

IFUPDOWN_VERSION=$(apt-cache show ifupdown | grep -Po '(?<=^Version: ).*')

if ! python <(cat<<EOF
from distutils.version import LooseVersion, StrictVersion
if LooseVersion("${IFUPDOWN_VERSION}") < LooseVersion("0.8.36"):
  exit(1)
EOF
              ); then
    echo "ifupdown version doesn't support 'client no' option!"
    exit ${LINENO}
fi

This is similar to calling python myscript.py.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.