0

I have this code:

print("Welcome to the NetBackup Symbolic Link Generator.\n")
print("Log into a server and run the command 'ls $ORACLE_BASE/admin'. Copy the list of folders (database names), but omit filenames and/or +ASM, then paste below.\n")

databases=input("Enter databases: ")
numNodes=input("Enter the number of nodes (1, 2, or 3): ")

print("\nCopy the output below and paste into the SSH session as \"orainst\".\n")

if int(numNodes) == 1:
    streams="1a 1b 1c"
elif int(numNodes) == 2:
    streams="1a 2a 1b 2b 1c 2c"
else:
    streams="1a 2a 3a 1b 2b 3b 1c 2c 3c"

db_list = databases.split()
streams_list= streams.split()

for db in db_list:
    print(db)

input("Press \"Enter\" to exit.")

Everything generally works, unless the user pastes something that contains a newline like:

dbone dbtwo tbtree
dbfour dbfive

And then I end up with this:

Welcome to the NetBackup Symbolic Link Generator.

Log into a server and run the command 'ls $ORACLE_BASE/admin'. Copy the list of folders (database names), but omit filenames and/or +ASM, then paste below.

Enter databases: dbone dbtwo tbtree
dbfour dbfive
Enter the number of nodes (1, 2, or 3): 
Copy the output below and paste into the SSH session as "orainst".

Traceback (most recent call last):
  File "C:\Users\en195d\Documents\Personal\Project\Python\NetBackupSymLinkGen.py", line 12, in <module>
    if int(numNodes) == 1:
ValueError: invalid literal for int() with base 10: 'dbfour dbfive'
>>> 

How can I handle input that contains newlines?

Rod Xavier
  • 3,983
  • 1
  • 29
  • 41
etho201
  • 151
  • 9
  • I would check out http://stackoverflow.com/questions/13128951/how-to-get-user-input-for-multiline-lines-in-python-3 – Joe Swindell May 12 '14 at 15:12
  • Joe - That is a good workaround but I guess I was hoping for something a little more transparent. It's too bad Python can't seem to tell the difference between a newline ('\n') and a carriage-return. http://en.wikipedia.org/wiki/Newline – etho201 May 12 '14 at 15:16
  • Yeah...I haven't written python in a while but you could also try stripping '\n' out of the input. Try .strip('\n') on your input – Joe Swindell May 12 '14 at 15:19
  • 1
    I've already tried that... "databases=input("Enter databases: ").strip('\n')" - I end up with the same error as shown above. – etho201 May 12 '14 at 15:28
  • 1
    IIRC, interpreting newlines as the enter key is a convenience for users that want to automate a program by pasting in all of their input at once. I don't know of any way to disable it, and it might be a tad user-hostile to do it if you could. Imagine a user saying, "this darn program always asks me for more input no matter what I paste in the first time!" – Kevin May 12 '14 at 15:51
  • *"How can I handle input that contains newlines?"* -- how do you want to separate different inputs from a user if a single input is allowed to contain newlines? How do you know that the input for "databases" ended and the second input for "number" begins? – jfs May 13 '14 at 19:06

2 Answers2

0

You could keep asking for input until the user enters a digit. This allows users to enter database names on multiple lines if they choose:

user_input = []
while len(user_input) == 0 or user_input[-1] not in ["1", "2", "3"]:
    response = input("Enter databases, followed by number of nodes (1, 2, or 3): ")
    user_input.extend(response.split())

numNodes = user_input.pop()
db_list = user_input

print(numNodes)
print(db_list)

Result:

Enter databases, followed by number of nodes (1, 2, or 3): dbone dbtwo tbtree
Enter databases, followed by number of nodes (1, 2, or 3): dbfour dbfive
Enter databases, followed by number of nodes (1, 2, or 3): 1
1
['dbone', 'dbtwo', 'tbtree', 'dbfour', 'dbfive']

One drawback to this approach is that the user might have a database named "1", and won't be able to enter it, since it will be interpreted as numNodes. Another alternative is to keep accepting db names until you recieve an empty line:

db_list = []
while True:
    response = input("Enter databases (press Enter when finished): ")
    if not response: break
    db_list.extend(response.split())

numNodes = input("Enter the number of nodes (1, 2, or 3):")

print(numNodes)
print(db_list)

Result:

Enter databases (press Enter when finished): dbone dbtwo tbtree
Enter databases (press Enter when finished): dbfour dbfive
Enter databases (press Enter when finished):
Enter the number of nodes (1, 2, or 3):1
1
['dbone', 'dbtwo', 'tbtree', 'dbfour', 'dbfive']
Kevin
  • 74,910
  • 12
  • 133
  • 166
0

When you call input(), it reads one line of data and returns that to your program. The second line is still sitting in the low-level libc input buffer. Strip('\n') didn't work because python hasn't seen the second line of data yet. Unfortunately, its difficult to see if there is more data in stdin. You can't peek, and if you read, it will just hang waiting for input for the other 99% of your users who cut/paste correctly.

Options to handle this use case are pretty messy and may mess up other use cases (for instance, what about a cut and paster who really did want the second line to be a response to the second question). Personally, I'd do what virtually all command line programs do: treat it as garbage-in/garbage-out and fail.

tdelaney
  • 73,364
  • 6
  • 83
  • 116