I have searched and searched and searched but have not found and answer to my particular situation, or if I had I don't know how to implement it. I'm trying to create a script using python and bash to automate my project creation process. Whereas I would run the testing.py "as it is called now because I'm testing" with 1 command line argument which would be the name of the project folder, then it would ask "where I would like to create the project" with two options for paths to store the project and store the path using an if statement into the path variable. then I would like to pass that variable in the bash script to navigate to it and create the project directory there. here is what I have so far:
so the command that I would run is like:
python testing.py newProject
in the .test.sh
#!/bin/bash
function testing() {
cd
desktop
python testing.py $1
}
and in the testing.py
import sys
import os
school = "/Users/albert/Open/Microverse/"
personal = "/Users/albert/Open/code/Projects/"
path = " "
userInput = input("What type of project would you like to create? Personal or School? ")
if userInput.lower() == "personal":
path = personal
elif userInput.lower() == "school":
path = school
def testing():
folderName = str(sys.argv[1])
os.makedirs(path + str(folderName))
print(folderName)
if __name__ == "__main__":
testing()
Use "$1" instead of $1 as shown below
python testing.py "$1"
You have to run your bash function like this
testing $1
The whole script
#!/bin/bash
function testing() {
cd
desktop
python test.py $1
}
testing $1
And change input() to raw_input() in your python part. Input() function returns will actually evaluate the input string and try to run it as Python code. And if i try to input Personal this give me an error
What type of project would you like to create? Personal or School? Personal
Traceback (most recent call last):
File "test.py", line 9, in <module>
userInput = input("What type of project would you like to create? Personal or School? ")
File "<string>", line 1, in <module>
NameError: name 'Personal' is not defined
And if you input personal than userInput will be /Users/albert/Open/code/Projects/ wich is not wat you want right?
I'm guessing desktop is not actually a valid command, and that you really want
testing () {
cd ~/Desktop
python test.py "$1"
]
A better design might be to not force all projects to be created inside your desktop folder, though.
testing () {
python ~/Desktop/test.py "$1"
}
will let you run the script in any directory.
A still better design would be to give the script a proper shebang, mark it executable, call it testing, and save it in your PATH; then you don't need a shell function at all.
A different and IMHO more usable approach would be to allow the user to specify "personal" or "school" as part of the command line, instead of forcing the script to use interactive input. Requiring interactive I/O makes the script harder to use as a building block in further automation, and robs the user of the ability to use the shell's history and completion features.
The following refactoring hard-codes a very crude option parser, and uses a dict to map the project type to a specific folder name.
#!/usr/bin/env python3
import sys
import os
paths = {
"school": "/Users/albert/Open/Microverse/"
"personal": "/Users/albert/Open/code/Projects/"
}
def makeproject(type, folder):
os.makedirs(os.path.join(path, folder))
def gettype():
while True:
userInput = input("What type of project would you like to create? Personal or School? ")
if userInput in paths:
return paths[userInput]
print("Not a valid option: {} -- try again".format(userInput))
def testing():
idx = 1
if sys.argv[idx] == "--school":
type = paths["school"]
idx = 1
elif sys.argv[idx] == "--personal":
type = paths["personal"
idx = 2
else:
type = gettype()
folderName = sys.argv[idx]
makeproject(type, folderName)
print(folderName)
if __name__ == "__main__":
testing()
Notice that sys.argv and the value returned by input are already strings; there is no need to call str() on them.
Related
(First off, apologies for the roughness of this question's writing-- would love any constructive feedback.)
Ok what I'm doing is a bit involved-- I'm trying to make a Python script that executes Bash scripts that each compile a component of a Linux From Scratch (LFS) system. I'm following the LFS 11.2 book pretty closely (but not 100%, although I've been very careful to check where my deviations break things. If you're familiar with LFS, this is a deviation that breaks things).
Basically, my script builds a bunch of tools (bash, tar, xz, make, gcc, binutils) with a cross compiler, and tells their build systems to install them into a directory lfs/temp-tools. Then the script calls os.chroot('lfs') to chroot into the lfs directory, and immediately resets all the environment variables (most importantly PATH) with:
os.environ = {"PATH" : "/usr/bin:/usr/sbin:/temp-tools/bin", ...other trivial stuff like HOME...}
But after the chroot, my calls of
subprocess.run([f"{build_script_path} >{log_file_path} 2>&1"], shell=True)
are failing with FileNotFoundError: [Errno 2] No such file or directory: '/bin/sh', even though
bin/sh in the chroot directory is a sym link to bash
there's a perfectly good copy of bash in /temp-tools/bin
calling print(os.environ) after the python chroot shows /temp-tools/bin is in PATH
I thought maybe subprocess.run is stuck using the old environment variables, before I reset them upon entering the chroot, but adding env=os.environ to subprocess.run does not help. :/ I'm stuck for now
For context if it helps, here is where the subprocess.run call gets made:
def vanilla_build(target_name, src_dir_name=None):
def f():
nonlocal src_dir_name
if src_dir_name == None:
src_dir_name = target_name
tarball_path = find_tarball(src_dir_name)
src_dir_path = tarball_path.split(".tar")[0]
if "tcl" in src_dir_path:
src_dir_path = src_dir_path.rsplit("-",1)[0]
snap1 = lfs_dir_snapshot()
os.chdir(os.environ["LFS"] + "srcs/")
subprocess.run(["tar", "-xf", tarball_path], check=True, env=os.environ)
os.chdir(src_dir_path)
build_script_path = f"{os.environ['LFS']}root/build-scripts/{target_name.replace('_','-')}.sh"
log_file_path = f"{os.environ['LFS']}logs/{target_name}"
####### The main call #######
proc = subprocess.run([f"{build_script_path} >{log_file_path} 2>&1"],
shell=True, env=os.environ)
subprocess.run(["rm", "-rf", src_dir_path], check=True)
if proc.returncode != 0:
red_print(build_script_path + " failed!")
return
tracked_file_record_path = f"{os.environ['LFS']}logs/tracked/{target_name}"
with open(tracked_file_record_path, 'w') as f:
new_files = lfs_dir_snapshot() - snap1
f.writelines('\n'.join(new_files))
f.__name__ = "build_" + target_name
return f
And how I enter the chroot:
def enter_chroot():
os.chdir(os.environ["LFS"])
os.chroot(os.environ["LFS"])
os.environ = {"HOME" : "/root",
"TERM" : os.environ["TERM"],
"PATH" : "/usr/bin:/usr/sbin:/temp-tools/bin",
"LFS" : '/'}
Thank you! In the meantime I'm going to chop away as much code as possible to isolate the problem to either understand whatever I'm not getting or rewrite this question to be less context specific
I want to make hotstrings in python that converts one word when typed into another after some processing, since AHK is very limiting when it comes to determining which word to type. Right now, I am using a hotstring in ahk that runs code on the command line that runs a python script with the word that I typed as arguments. Then I use pyautogui to type the word. However, this is very slow and does not work when typing at speed. I'm looking for a way to do this all with python and without ahk, but I have not found a way to do hotstrings in python. For example, every time I type the word "test" it replaces it with "testing." Thanks for your help. I'm running the latest version of Python and Windows 10 if that is useful to anyone by the way.
(if you want to process it as each letter is typed(t,te,tes,test), you should edit your question)
I call my SymPy functions using ahk hotkeys. I register the python script as a COM server and load it using ahk.
I do not notice any latency.
you'll need pywin32, but don't download using pip install pywin32
download from https://github.com/mhammond/pywin32/releases
OR ELSE IT WON'T WORK for AutoHotkeyU64.exe, it will only work for AutoHotkeyU32.exe.
make sure to download amd64, (I downloaded pywin32-300.win-amd64-py3.8.exe)
here's why: how to register a 64bit python COM server
toUppercase COM server.py
class BasicServer:
# list of all method names exposed to COM
_public_methods_ = ["toUppercase"]
#staticmethod
def toUppercase(string):
return string.upper()
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
print("Error: need to supply arg (""--register"" or ""--unregister"")")
sys.exit(1)
else:
import win32com.server.register
import win32com.server.exception
# this server's CLSID
# NEVER copy the following ID
# Use "print(pythoncom.CreateGuid())" to make a new one.
myClsid="{C70F3BF7-2947-4F87-B31E-9F5B8B13D24F}"
# this server's (user-friendly) program ID
myProgID="Python.stringUppercaser"
import ctypes
def make_sure_is_admin():
try:
if ctypes.windll.shell32.IsUserAnAdmin():
return
except:
pass
exit("YOU MUST RUN THIS AS ADMIN")
if sys.argv[1] == "--register":
make_sure_is_admin()
import pythoncom
import os.path
realPath = os.path.realpath(__file__)
dirName = os.path.dirname(realPath)
nameOfThisFile = os.path.basename(realPath)
nameNoExt = os.path.splitext(nameOfThisFile)[0]
# stuff will be written here
# HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\${myClsid}
# HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{C70F3BF7-2947-4F87-B31E-9F5B8B13D24F}
# and here
# HKEY_LOCAL_MACHINE\SOFTWARE\Classes\${myProgID}
# HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Python.stringUppercaser
win32com.server.register.RegisterServer(
clsid=myClsid,
# I guess this is {fileNameNoExt}.{className}
pythonInstString=nameNoExt + ".BasicServer", #toUppercase COM server.BasicServer
progID=myProgID,
# optional description
desc="return uppercased string",
#we only want the registry key LocalServer32
#we DO NOT WANT InProcServer32: pythoncom39.dll, NO NO NO
clsctx=pythoncom.CLSCTX_LOCAL_SERVER,
#this is needed if this file isn't in PYTHONPATH: it tells regedit which directory this file is located
#this will write HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{C70F3BF7-2947-4F87-B31E-9F5B8B13D24F}\PythonCOMPath : dirName
addnPath=dirName,
)
print("Registered COM server.")
# don't use UseCommandLine(), as it will write InProcServer32: pythoncom39.dll
# win32com.server.register.UseCommandLine(BasicServer)
elif sys.argv[1] == "--unregister":
make_sure_is_admin()
print("Starting to unregister...")
win32com.server.register.UnregisterServer(myClsid, myProgID)
print("Unregistered COM server.")
else:
print("Error: arg not recognized")
you first need to register the python COM server:
first, get your own CLSID: just use a python shell.
import pythoncom
print(pythoncom.CreateGuid())
then, set myClsid to that output
to register:
python "toUppercase COM server.py" --register
to unregister:
python "toUppercase COM server.py" --unregister
hotstring python toUppercase.ahk
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
#SingleInstance, force
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
SetBatchLines, -1
#KeyHistory 0
ListLines Off
#Persistent
#MaxThreadsPerHotkey 4
pythonComServer:=ComObjCreate("Python.stringUppercaser")
; OR
; pythonComServer:=ComObjCreate("{C70F3BF7-2947-4F87-B31E-9F5B8B13D24F}") ;use your own CLSID
; * do not wait for string to end
; C case sensitive
:*:hello world::
savedHotstring:=A_ThisHotkey
;theActualHotstring=savedHotstring[second colon:end of string]
theActualHotstring:=SubStr(savedHotstring, InStr(savedHotstring, ":",, 2) + 1)
send, % pythonComServer.toUppercase(theActualHotstring)
return
f3::Exitapp
you can test the speed of hotstring hello world, it's very fast for me.
Edit def toUppercase(string): to your liking
So I am writing a script to automate some things that me and my teammates do. We have a git repo and this script is intended for all members to use. It has a part that is hardcoded to specifically be my folder path: C:/Users/jorge.padilla/etc...
I am still relatively new to python and not familiar with all the different libraries. I basically want to turn the user directory, i.e. jorge.padilla, into a variable that is not hardcoded, and that doesn't need to take user input so that the script will search for whatever the current user directory is and substitute it.
Below is a small snippet of the automation script I am writing to use as an example.
import os, sys
from pathlib import Path
from enum import Enum
#Global Variables
PRODUCTS_FOLDER = "Products"
APP_FOLDER = "App"
DEV_BUILD = "ionic cordova build android"
PROD_BUILD = "ionic cordova build android --release --prod"
class BuildConfig():
def __init__(self, start_path):
self.start_path = start_path
def getProductFolder(self):
return os.path.join(self.start_path, PRODUCTS_FOLDER)
class BuildTypeEnum(Enum):
PROD = 1
DEV = 2
def buildingApp(ConfigPath:BuildConfig, DEVvPROD:BuildTypeEnum):
path = ConfigPath.getProductFolder()
app_path = os.path.join(path, APP_FOLDER)
os.chdir(app_path)
if DEVvPROD == BuildTypeEnum.DEV:
os.system(DEV_BUILD)
elif DEVvPROD == BuildTypeEnum.PROD:
os.system(PROD_BUILD)
else:
print("Invalid input.")
return
if __name__ == "__main__":
root_start_path = "C:/Users/jorge.padilla/Documents/"
build = BuildConfig(root_start_path)
buildType = None
buildTypeInput = input("Is this a dev or production build? (d/p): ")
if (buildTypeInput.lower() == 'd'):
buildType = BuildTypeEnum.DEV
elif (buildTypeInput.lower() == 'p'):
buildType = BuildTypeEnum.PROD
else:
print("Please specify if this is a development or production build.")
return
The main variable I want to do this for is root_start_path
You should use pathlib (which you imported, but never used?):
import pathlib
root_start_path = pathlib.Path.home() # WindowsPath('C:/Users/jorge.padilla')
It works across platforms as well, and it's really the best way to handle file paths (IMO)
It can even simplify the syntax in accessing other directories within that path:
root_start_path = pathlib.Path.home() / 'Documents' # WindowsPath('C:/Users/jorge.padilla/Documents')
You could also do:
from os.path import expanduser
home = expanduser("~")
I'm trying to use python to make symbolic links. When I try to use os.symlink I get a WinError 5 "Access is denied" error, but if I try to create the same symlink using mklink, it works fine. (I'm running this as an administrator, and have the 'Create symbolic links' privilege.)
I then to use subprocess.call to call mklink directly with the proper arguments and such, and it runs without any errors, yet when it's finished the symlinks have not been created.
When I try to use Popen to run the command I get a 'system cannot find the file specified' error even though the directory is there.
This is on Windows Server 2008.
def createStudentFolders(studentList):
grad_year = input("Please enter the year of graduation for the class of students being created (i.e. 2016): ")
for student in studentList:
student = student.replace(" ","") # Gets rid of the white space in the name
studentPath = 'cpwd/{}'.format(student)
try:
os.mkdir(studentPath)
except:
pass
print(studentPath)
proc = Popen("icacls {} /inheritance:e /grant STUDENT\{}:(OI)(CI)F /T".format(studentPath, student))
classFolder = "students/ClassOf{}".format(grad_year)
try:
os.mkdir(classFolder)
except:
pass
os.symlink('{}/{}'.format(classFolder, student), studentPath, target_is_directory=True)
print("Done")
def main():
newStudentsLoc = "newStudents.txt"
studentList = readStudentsToList(newStudentsLoc)
createStudentFolders(studentList)
main()
input("Pausing")
File resides in Z:\
inside that are
cpwd\<student folders>
students\ClassOf<Year>\<symlinks to student folders go here>
I've tried using absolute paths as well with no luck.
I'm trying to make a secret santa Python script. I have a VM running Ubuntu, in which I ran the script, no problems there. Next step, I wanted to run it in PowerShell. I have Python installed, version 2.7.10 (checked vis PowerShell). I have run other scripts before (and even different versions of this script) in PowerShell before, so I know I have that set up correctly. But for some reason, this one won't run. I even put a print statement as the first real line after the imports, but it isn't reached. Am I using a built in function that doesn't work with Windows?
import random
import pprint
people = ["Billy", "Joe", "Bob", "Sam", "John", "Kyle"]
chosen = {}
preventions = [["Joe", "Bob"], ["Sam", "John"]]
print "Test"
for person in people:
groupExists = False
for preventionGroup in preventions:
if person in preventionGroup:
groupExists = True
if not groupExists:
preventions.append([person])
for preventionGroup in preventions:
for person in preventionGroup:
while True:
# print "Hi"
num = random.randrange(0,6)
# print num
giftee = people[num]
if giftee not in preventionGroup and giftee not in list(chosen.values()):
chosen[person] = giftee
break
# pprint.pprint(chosen)
for key, value in chosen.iteritems():
print "Gifter: " + key
raw_input()
print "Giftee: " + value
raw_input()
It's named Secret Santa.py, so the PowerShell line to run the script is
python '.\Secret Santa.py'
And yes, I have checked to make sure I'm in the same directory as the file.