0

I'm trying to redirect the output of an Nmap scan to a text file using Python.

Here's my code:

outputName = raw_input("What is the output file name?")
fname = outputName
with open(fname, 'w') as fout:
     fout.write('')

command = raw_input("Please enter an Nmap command with an IP address.")
args = shlex.split(command)
proc = subprocess.Popen(args,stdout=fname)

The error:

Traceback (most recent call last):
  File "mod2hw4.py", line 17, in <module>
    proc = subprocess.Popen(args,stdout=fname)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 701, in __init__
    errread, errwrite), to_close = self._get_handles(stdin, stdout, stderr)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1127, in _get_handles
    c2pwrite = stdout.fileno()
AttributeError: 'str' object has no attribute 'fileno'
moooeeeep
  • 31,622
  • 22
  • 98
  • 187
Asa Hunt
  • 129
  • 4
  • 10
  • Also see [How to redirect output with subprocess in Python?](https://stackoverflow.com/q/4965159/608639). – jww Feb 16 '19 at 23:44

2 Answers2

3

As Paulo mentioned above, you have to pass an open file; the name of the file won't work. You should probably do this with the same context you created (the with block); try rearranging it to this:

outputName = raw_input("What is the output file name?")
fname = outputName

command = raw_input("Please enter an Nmap command with an IP address.")
args = shlex.split(command)

with open(fname, 'w') as fout:
    proc = subprocess.Popen(args,stdout=fout)
    return_code = proc.wait()

Not that subprocess.Popen is called with stdout=fout now instead of stdout=fname. The context manager created by the with statement ensures the file will be closed when the nmap process is done, even if an exception occurs.

wrigby
  • 126
  • 4
  • note: the file may be closed in the parent *before* nmap finishes because `Popen` does *not* wait for the child process to exit. `nmap` inherits its own copy of the file descriptor. – jfs Feb 05 '15 at 12:30
  • @J.F.Sebastian is right - you should explicitly wait for the process to finish first. I've added a call to `subprocess.wait` to do that in the with block. – wrigby Feb 06 '15 at 00:22
1

From the docs:

stdin, stdout and stderr specify the executed program’s standard input, standard output and standard error file handles, respectively. Valid values are PIPE, an existing file descriptor (a positive integer), an existing file object, and None.

So a filename is not a valid value for the stdout argument.

I guess you want this instead:

proc = subprocess.Popen(args,stdout=open(fname, 'w'))

Or better yet, just keep everything within the with block:

with open(fname, 'w') as fout:
    fout.write('')

    command = raw_input("Please enter an Nmap command with an IP address.")
    args = shlex.split(command)
    proc = subprocess.Popen(args,stdout=fout)
Paulo Scardine
  • 73,447
  • 11
  • 124
  • 153
  • You mean `check_output`. Agree (there was a since deleted comment about this with a small typo: `checked_output`). – Paulo Scardine Feb 04 '15 at 20:42
  • Thanks so much! I read the description in the documentation but I didn't understand that the name of an open file and a file object weren't the same thing, hence the error. So fout is an actual file object then, just so I'm clear? – Asa Hunt Feb 04 '15 at 22:43
  • Yes, it is. An interesting thing about open files in Python is that they adhere to the "context management protocol", so when you use them at the `with` statement they are guaranteed to be automatically closed at the end of the block (so your program does not leak resources - most operational systems limit the number of open files).. – Paulo Scardine Feb 04 '15 at 22:52
  • I hate to be a bother, but the subprocess with the actual Nmap command isn't executing inside the with block when I run the program. Any ideas? – Asa Hunt Feb 05 '15 at 02:48
  • We are here to help. Inspect the value of `proc`, may be `proc.returncode` would give you a clue. – Paulo Scardine Feb 05 '15 at 11:58