Pythonic way to traverse etc shadows - python

I am trying to implement a logic using python :
cat /etc/shadow | awk -F: '($2 == "" ) { print $1 " does not have a password "}'
If the above returns the output for the user i will do
passwd -l <username>
I am trying to implement the above logic using python , but i am not really sure if it is working out in that way; here is my python code:
/etc/shadow looks like
root:*:17709:0:99999:7:::
daemon:*:17709:0:99999:7:::
bin:*:17709:0:99999:7:::
sys:*:17709:0:99999:7:::
sync:*:17709:0:99999:7:::
games:*:17709:0:99999:7:::
man:*:17709:0:99999:7:::
lp:*:17709:0:99999:7:::
mail:*:17709:0:99999:7:::
news:*:17709:0:99999:7:::
uucp:*:17709:0:99999:7:::
proxy:*:17709:0:99999:7:::
www-data:*:17709:0:99999:7:::
backup:*:17709:0:99999:7:::
CODE
with open("/etc/shadow") as file:
for line in file:
line = line.rstrip()
if line[line.find(":")+1:line.find(":")]=="":
print "This is a problem"
elif line[line.find(":")+1:line.find(":")]=="*":
print line[line.find(":")+1:line.find(":")]
else:
print "All Good"
The above code returns "This is a problem" , which isn't right

You can use re to extract desired column:
import re
data = """root:*:17709:0:99999:7:::
daemon:*:17709:0:99999:7:::
bin:*:17709:0:99999:7:::
sys:*:17709:0:99999:7:::
sync:*:17709:0:99999:7:::
games:*:17709:0:99999:7:::
man:*:17709:0:99999:7:::
lp:*:17709:0:99999:7:::
mail:*:17709:0:99999:7:::
news:*:17709:0:99999:7:::
uucp:*:17709:0:99999:7:::
proxy:*:17709:0:99999:7:::
www-data:*:17709:0:99999:7:::
backup:*:17709:0:99999:7:::"""
groups = re.findall('(.*?):(.*?):(.*?):(.*?):(.*?):(.*?):(.*?):(.*?):', data)
if all(g[1].strip() for g in groups):
print('All good')
else:
print('This is a problem')
This prints:
All good
Explanation of this regex here. In the second group (g[1]), you have the shadowed password (*), or empty string.

Try it:
with open("/etc/shadow/") as ff:
for line in ff:
login_name,pwd,remainder=line.split(":",maxsplit=2)
print(login_name,pwd) # change it as you like

Just split your lines on the ":" separator and check the value at the second position (which is at index 1 of course):
data = """
root:*:17709:0:99999:7:::
daemon:*:17709:0:99999:7:::
bin:*:17709:0:99999:7:::
sys:*:17709:0:99999:7:::
sync:*:17709:0:99999:7:::
games:*:17709:0:99999:7:::
man:*:17709:0:99999:7:::
lp:*:17709:0:99999:7:::
mail:*:17709:0:99999:7:::
news:*:17709:0:99999:7:::
uucp:*:17709:0:99999:7:::
proxy:*:17709:0:99999:7:::
www-data:*:17709:0:99999:7:::
backup:*:17709:0:99999:7:::
"""
for line in data.strip().splitlines():
row = [part.strip() for part in line.split(":")]
if row[1] == "":
print("this is a problem")
elif row[1] == "*":
print row[1]
else:
print "all good"

Related

replace function not working with list items

I am trying to use the replace function to take items from a list and replace the fields below with their corresponding values, but no matter what I do, it only seems to work when it reaches the end of the range (on it's last possible value of i, it successfully replaces a substring, but before that it does not)
for i in range(len(fieldNameList)):
foo = fieldNameList[i]
bar = fieldValueList[i]
msg = msg.replace(foo, bar)
print msg
This is what I get after running that code
<<name>> <<color>> <<age>>
<<name>> <<color>> <<age>>
<<name>> <<color>> 18
I've been stuck on this for way too long. Any advice would be greatly appreciated. Thanks :)
Full code:
def writeDocument():
msgFile = raw_input("Name of file you would like to create or write to?: ")
msgFile = open(msgFile, 'w+')
msg = raw_input("\nType your message here. Indicate replaceable fields by surrounding them with \'<<>>\' Do not use spaces inside your fieldnames.\n\nYou can also create your fieldname list here. Write your first fieldname surrounded by <<>> followed by the value you'd like to assign, then repeat, separating everything by one space. Example: \"<<name>> ryan <<color>> blue\"\n\n")
msg = msg.replace(' ', '\n')
msgFile.write(msg)
msgFile.close()
print "\nDocument written successfully.\n"
def fillDocument():
msgFile = raw_input("Name of file containing the message you'd like to fill?: ")
fieldFile = raw_input("Name of file containing the fieldname list?: ")
msgFile = open(msgFile, 'r+')
fieldFile = open(fieldFile, 'r')
fieldNameList = []
fieldValueList = []
fieldLine = fieldFile.readline()
while fieldLine != '':
fieldNameList.append(fieldLine)
fieldLine = fieldFile.readline()
fieldValueList.append(fieldLine)
fieldLine = fieldFile.readline()
print fieldNameList[0]
print fieldValueList[0]
print fieldNameList[1]
print fieldValueList[1]
msg = msgFile.readline()
for i in range(len(fieldNameList)):
foo = fieldNameList[i]
bar = fieldValueList[i]
msg = msg.replace(foo, bar)
print msg
msgFile.close()
fieldFile.close()
###Program Starts#####--------------------
while True==True:
objective = input("What would you like to do?\n1. Create a new document\n2. Fill in a document with fieldnames\n")
if objective == 1:
writeDocument()
elif objective == 2:
fillDocument()
else:
print "That's not a valid choice."
Message file:
<<name>> <<color>> <<age>>
Fieldname file:
<<name>>
ryan
<<color>>
blue
<<age>>
18
Cause:
This is because all lines except the last line read from the "Fieldname" file contains "\n" characters. So when the program comes to the replacing part fieldNameList , fieldValueList and msg looks like this:
fieldNameList = ['<<name>>\n', '<<color>>\n', '<<age>>\n']
fieldValueList = ['ryan\n', 'blue\n', '18']
msg = '<<name>> <<color>> <<age>>\n'
so the replace() function actually searches for '<<name>>\n','<<color>>\n','<<age>>\n' in msg string and only <<age>> field get replaced.(You must have a "\n" at the end of msg file, otherwise it won't be replaced as well).
Solution:
use rstrip() method when reading lines to strip the newline character at the end.
fieldLine = fieldFile.readline().rstrip()

retrieving multiple json data in for loop using python

Thanks.
Am trying to get the particular key value from json data using python. logic is to call the function and function should return key (json output sometimes will be just 1 index data or sometime morethan 1 index data)
I was able to get the data and print them, it works fine in inside for loop but when I return back to main then am getting only one value. not sure if something to do with for loop.
json data :
[{
id: "587e569472",
hostname: "I-56BXX",
env: "Beta",
site: "I",
version: "2.0.0.38-1"},
{
id: "587e64472",
hostname: "I-56AXX",
env: "Beta",
site: "I",
version: "2.0.0.39-1"}]
main script :
def get_jsondata(url, hosts):
u = urllib2.urlopen(url)
json_object = json.load(u)
u.close
indexcount = len(json_object)
#print indexcount
#for i in json_object:
#print i['hostname']
if json_object == []:
print 'No Data!'
else:
for rows in json_object:
#print 'hostname:' + rows['hostname']
#print 'env:' + rows['env']
print 'hostname and its env:' + rows['hostname'] + " " + rows['env']
#return rows['hostname']
hosts = rows['hostname']
#print hosts
return (hosts)
#if __name__ == '__main__':
# main()
#main section
url = 'http://api.com/AppData/'
hosts = ""
hosts = get_jsondata(url, hosts)
#print "The required hostname " + str(hostname) + " and its env is " + str(venue)
print(hosts)
After running the script am getting output as :
hostname and its env:I-56BXX I
I-56BXX
I was trying to get both hostname return back to main so, output would be like
hostname and its env:I-56BXX I
hostname and its env:I-56AXX I
I-56BXX
I-56AXX
first 2 line from above output is from print stmt inside for loop and next 2 lines are from return data.
Well, your return statement is inside the loop, so it return on the first iteration, you can use thing like yield or stocking your result into a list you would return at the end of the function
someting like
return [row['hostname'] for row in json_object]
The reason you are only printing one record in json_object is because you are returning hosts too soon. If you remove the line return (hosts) from inside the for loop, then all records from json_object will be printed.
The format that you are hoping to end up with will require a little more work. The format that will be printed will look like this:
hostname and its env:I-56BXX I
I-56BXX
hostname and its env:I-56AXX I
I-56AXX
If you would like to print in your stated format, you should have one for loop that prints the first message and a second for loop to print the second message.
Create an empty list, append to the list, then move your return outside the loop. The other answers are cleaner and great, but here is the most direct fix of your code:
def get_jsondata(url, hosts):
u = urllib2.urlopen(url)
json_object = json.load(u)
u.close
indexcount = len(json_object)
#print indexcount
#for i in json_object:
#print i['hostname']
if json_object == []:
print 'No Data!'
else:
hosts = []
for rows in json_object:
#print 'hostname:' + rows['hostname']
#print 'env:' + rows['env']
print 'hostname and its env:' + rows['hostname'] + " " + rows['env']
#return rows['hostname']
hosts.append(rows['hostname'])
#print hosts
return (hosts)
Please do not post code with comment on StackOverflow: it's hard to read.
Keep your snippets simple. Plus, your code is not very clean (parenthesis at return statement, inconsistency with parenthesis on print statements, missing parenthesis on function calls u.close()...)
With that said, get_jsondata only output one value because of your return statement in your for loop. The return instruction breaks the loop during the first iteration.
Now I didn't get what the get_jsondata is supposed to return. By reading your code I guess it's a list of hostnames. In order to return the list of all the hostnames in your json data, you can use one powerful feature of python: list comprehension.
e.g
[x['hostname'] for x in json_object]

Python: How can I search a text file for a string input by the user that contains an integer?

This is an example of what is on the text file that I am searching:
15 - Project `enter code here`Name
APP_IDENTIFIER=ie.example.example
DISPLAY_NAME=Mobile Banking
BUNDLE_VERSION=1.1.1
HEADER_COLOR=#72453h
ANDROID_VERSION_CODE=3
20 - Project Name
APP_IDENTIFIER=ie.exampleTwo.exampleTwp
DISPLAY_NAME=More Mobile Banking
BUNDLE_VERSION=1.2.3
HEADER_COLOR=#23456g
ANDROID_VERSION_CODE=6
If, for example, the user types in 15, I want python to copy the following info:
ie.example.example
Mobile Banking
1.1.1
#72453h
3
because I need to copy it into a different text file.
I get the user to input a project number (in this example the project numbers are 15 & 20) and then I need the program to copy the app_identifier, display_name, bundle_version and android_version of the project relating to the number that the user input.
How do I get python to search the text file for the number input by the user and only take the needed information from the lines directly below that specific project?
I have a whole program written but this is just one section of it.
I don't really have any code yet to find and copy the specific information I need.
Here is code i have to search for the project ID
while True:
CUID = int(input("\nPlease choose an option:\n"))
if (CUID) == 0:
print ("Project one")
break
elif (CUID) == 15:
print ("Project two")
break
elif (CUID) == 89:
print ("Project three")
break
else:
print ("Incorrect input")
The solution thanks to Conor:
projectFile = open("C:/mobileBuildSettings.txt" , "r")
for line in projectFile:
CUID = str(CUID)
if CUID + " - " in line:
appIdentifier = next(projectFile).split("=")[1]
displayName = next(projectFile).split("=")[1]
bundleVersion = next(projectFile).split("=")[1]
next(projectFile)
androidVersionCode = next(projectFile).split("=")[1]
print (appIdentifier, displayName, bundleVersion, androidVersionCode)
break
projectfile = open("projects", "r")
for line in projectfile:
if CUID in line:
appIdentifier = next(projectfile).split("=")[1]
displayName = next(projectfile).split("=")[1]
bundleVersion = next(projectfile).split("=")[1]
next(projectfile)
androidVersionCode = next(projectfile).split("=")[1]
# Do whatever with the 4 values here, call function etc.
break
Then do with appIdentifier, displayName, bundleVersion & androidVersionCode what you will, they will return just the values after the '='.
Although I would recommend against generically searching for an integer, what if the integer is also in the bundle or android version?
There is no reason to list all individual numbers in a long if..else list. You can use a regular expression to check if a line starts with any digit. If it does, check if it matches the number you are looking for, and if it does not, skip the following lines until you reach your blank line separator.
As soon as you have the data you are looking for, you can use a regular expression again to locate the =, or simply use .find:
import re
numberToLookFor = '18'
with open("project.txt") as file:
while True:
line = file.readline()
if not line:
break
line = line.rstrip('\r\n')
if re.match('^'+numberToLookFor+r'\b', line):
while line and line != '':
if line.find('='):
print line[line.find('=')+1:]
line = file.readline().rstrip('\r\n')
else:
while line and line != '':
line = file.readline().rstrip('\r\n')
Here you go:
while True:
CUID = int(input("\nPlease choose an option:\n"))
if (CUID) == 0:
appid = value.split("APP_IDENTIFIER=")[1] # get the value after "APP_IDENTIFIER="
print appid
output >>> ie.example.example
You can apply the same code for all values there, just change the title before "=".
Get the whole line from text then get only the value after "=" with this code for result output.

else: syntax is incorrect

I am a little new to python and I am trying to write this script to cancel print jobs over 1 mb.. (the line where it is checking for size is set to 1 mb just to make sure it is working). for some reason my last else statement keeps saying it has invalid syntax. I checked to see if all parentheses were closed and I could not find an unmatched pair. could someone please tell me why it says it is invalid? Also can you take a look at my line 24 (fullname = ...grep...) to make sure the syntax on that is correct?
#! /usr/bin/python
import os
infile = open ('test.pl', 'r')
outfile = open('print.reportpython', 'w+')
newfile = infile.readlines()
newfile.pop(0)
count = 0
firstline = newfile[0]
splitline = firstline.split()
currentuser = splitline[1]
currentuser = str(currentuser)
for line in newfile:
newline = line.split()
names = newline[1]
size = int(newline[2])
names = str(names)
print names
if names is currentuser:
if size >= 1:
os.popen ("cancel lab01-10292")
fullname = os.popen("cat /etc/passwd |grep " + newline[1] + "cut -d':' -f5")
count += 1
print count
else:
print outfile.write ("(" + currentuser + ")")
print outfile.write (" ")
count = 0
currentuser = names
You do:
if foo:
bar
baz
else:
bomb
Which is wrong. All the lines between the if and its corresponding else must be indented deeper than the if and else, like this:
if foo:
bar
baz
else:
bomb
The else is at the same indentation as the previous line, but the statement on the previous line doesn't have an else clause. Fix your indentation.

Conditional elif statement is not yielding the correct results

The elif stament should print the log files and path that were not found in a search that I conduct. However, they yield every line that is searched in a single file (a plethora of info). What am I doing wrong?
for line in fileinput.input(walk_dir(directory, (".log", ".txt"))):
result = regex.search(whitespace.sub('', line))
if result:
template = "\nLine: {0}\nFile: {1}\nString Type: {2}\n\n"
output = template.format(fileinput.filelineno(), fileinput.filename(), result.group())
print output
temp.write(output)
break
elif not result:
template = "\nLine: {0}\nString not found in File: {1}\nString Type: {2}\n\n"
output = template.format(fileinput.filelineno(), fileinput.filename(), result.group())
print output
temp.write(output)
else:
print "There are no files in the directory!!!"
Actual Code:
elif searchType =='2':
print "\nDirectory to be searched: " + directory
print "\nFile result2.log will be created in: c:\Temp_log_files."
paths = "c:\\Temp_log_files\\result2.log"
temp = file(paths, "w")
userstring = raw_input("Enter a string name to search: ")
userStrHEX = userstring.encode('hex')
userStrASCII = ''.join(str(ord(char)) for char in userstring)
regex = re.compile(r"(%s|%s|%s)" % ( re.escape( userstring ), re.escape( userStrHEX ), re.escape( userStrASCII )))
goby = raw_input("Press Enter to begin search (search ignores whitespace)!\n")
def walk_dir(directory, extensions=""):
for path, dirs, files in os.walk(directory):
for name in files:
if name.endswith(extensions):
yield os.path.join(path, name)
whitespace = re.compile(r'\s+')
for line in fileinput.input(walk_dir(directory, (".log", ".txt"))):
result = regex.search(whitespace.sub('', line))
if result:
template = "\nLine: {0}\nFile: {1}\nString Type: {2}\n\n"
output = template.format(fileinput.filelineno(), fileinput.filename(), result.group())
print output
temp.write(output)
#break
elif result not in line:
output = fileinput.filename()
print output
temp.write(output)
break
else:
print "There are no files in the directory!!!"
You're iterating over every line of every file passed to fileinput.input(...), right? And you perform the if statement for every line. If the condition is true, then you break, but if the condition is false, you don't break, but write to temp. So for every line in fileinput.input that doesn't match the condition, you write a line to temp and print output. (Actually, the above is wrong -- see edit below.)
Also, elif str(result) not in line: will have strange results -- just use else as others have suggested. If result evaluates to false in this situation, then result == None, which means that str(result) == 'None', which means that if a line contains None, then you'll have unexpected results.
Edit: Ok, actually, looking more closely at your actual code the above is wrong, strictly speaking. But the point remains -- fileinput.input() returns a FileInput object that in essence concatenates the files and iterates over every line in turn. Since in some cases you don't want to perform an action per line, but per file, you'll have to iterate over them individually. You could do this without fileinput but since that's what you're using, we'll stick with that:
for filename in walk_dir(directory, (".log", ".txt")):
for line in fileinput.input(filename):
result = regex.search(whitespace.sub('', line))
if result:
template = "\nLine: {0}\nFile: {1}\nString Type: {2}\n\n"
output = template.format(fileinput.filelineno(), fileinput.filename(), result.group())
print output
break # (assuming you only want to print the first result)
else:
ouput = fileinput.filename()
print output
temp.write(output)
break
The way this works: for every file in the list, this prints the first match in the file, or prints the filename if no match was found. You can use else with a for loop in python; the else block at the end of the loop is executed if the loop is not broken. Since no match was found, the filename is printed.
If you wanted to print out all matches in a file, you could save the matches in a list, and instead of using else, you could test the list. Simplified example:
matches = []
for line in fileinput.input(filename):
if searchline(line):
matches.append(line)
if matches:
print template.format(matches)
else:
print fileinput.filename()

Categories