string variable of cwd - python

Input
import os
my_cwd = str(os.system("cd"))
Output
C:\ProgramData\Anaconda2
Input
my_cwd
Output
'0'
I would expect calling my_cwd would return 'C:\ProgramData\Anaconda2' what am I missing?

os.system returns the return code of the command as an integer (that's why you tried to convert to str), not the output of the command as a string.
To get the output, you could use subprocess.check_output (subprocess.run in python 3.5+) with shell=True since cd is built-in:
import subprocess
value = subprocess.check_output(["cd"],shell=True)
(check_output raises an exception if the command fails, though)
You also have to "cleanup" the output by using value.rstrip() and decode the result into a string, since subprocess.check_output returns a bytes object... Also, your code is not portable on Linux, since the required command would be pwd.
Well, that very complex to just get the current directory (leave that kind of stuff for cls or clear commands). The most pythonic way to get it is to use:
os.getcwd()

Related

How to create a path for the argument cwd in subprocess.run

I have the following paths:
airflow_home = os.path.join("opt", "airflow")
airflow_docs = os.path.join(airflow_home, "docs")
And I want airflow_docs path to be used within a bash command. For that, I have used the following code:
subprocess.run([f"sphinx-apidoc -o ./ ../plugins"],
shell=True,
cwd=airflow_docs)
And I get an error FileNotFoundError.
However, this does work:
subprocess.run([f"sphinx-apidoc -o ./ ../{doc_module}"],
shell=True,
cwd="/opt/airflow/docs")
So it seems that a missing leading slash is causing the problem. I have searched in google about adding a leading slash to a path with no success. So, is it possible to use os.path package for subprocess.run, or do I have to use a hardcoded string?
If you want a slash, put a slash.
airflow_home = os.path.join("/opt", "airflow")
But of course, having Python glue together the strings is not really useful. Indeed, the result of os.path.join is simply a string, equivalent to a hard-coded string. So just write it out:
airflow_home = "/opt/airflow"
Or if you want to do this in Python, perhaps prefer pathlib:
airflow_home = pathlib.Path("/opt") / "airflow"
As an aside, your subprocess code is broken; you want to pass either a string, with shell=True, or a list of tokens, without shell=True. (Windows "helpfully" hides this error but it's still wrong.)
subprocess.run(
["sphinx-apidoc", "-o", "./", "../plugins"],
cwd=airflow_docs)
subprocess conveniently allows you to pass in a pathlib.Path object as the value of cwd, though this might not always have been the case, if you need to support older versions of Python.
You probably want to add check=True to have Python raise an error if the subprocess fails. Perhaps see also Running Bash commands in Python

Bash script will not run with subprocess in Python

For some reason, no matter how many variations I've tried, I can't seem to execute a bash script I've written. The command words 100% fine in Terminal, but when I try calling it with a subprocess, it returns nothing.
from os import listdir
import subprocess
computer_name = 'homedirectoryname'
moviefolder = '/Users/{}/Documents/Programming/Voicer/Movies'.format(computer_name)
string = 'The lion king'
for i in listdir(moviefolder):
title = i.split('.')
formatted_title = title[0].replace(' ', '\ ')
if string.lower() == title[0].lower():
command = 'vlc {}/{}.{}'.format(moviefolder, formatted_title, title[1])
subprocess.call(["/usr/local/bin",'-i','-c', command], stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
else:
continue
The bash executable file looks like this:
#/bin/bash
func() {
open -a /Applications/VLC.app/Contents/MacOS/VLC $1
}
Where have I gone wrong?
You should call open directly:
import os
import subprocess
computer_name = 'homedirectoryname'
moviefolder = '/Users/{}/Documents/Programming/Voicer/Movies'.format(computer_name)
string = 'The lion king'
for filename in os.listdir(moviefolder):
title = filename.split('.')
if string.lower() == title[0].lower():
subprocess.call(['open', '-a', '/Applications/VLC.app/Contents/MacOS/VLC', os.path.join(moviefolder, filename)])
Since you are using shell=True, the command must be a string:
On Unix with shell=True, the shell defaults to /bin/sh. If args is a
string, the string specifies the command to execute through the shell.
This means that the string must be formatted exactly as it would be
when typed at the shell prompt. This includes, for example, quoting or
backslash escaping filenames with spaces in them. If args is a
sequence, the first item specifies the command string, and any
additional items will be treated as additional arguments to the shell
itself. (docs)
Like you even mentioned in a comment, you get /usr/local/bin: is a directory when you properly capture the error from the shell (and take out the erroneous shell=True; or correspondingly refactor the command line to be suitable for this usage, i.e. pass a string instead of a list).
Just to spell this out, you are attempting to run the command /usr/local/bin with some options; but of course, it's not a valid command; so this fails.
The actual script you seem to want to run will declare a function and then exit, which results in the function's definition being lost again, because the subprocess which ran the shell in which this function declaration was executed has now terminated and released all its resources back to the system.
Perhaps you should take more than just a few steps back and explain what you actually want to accomplish; but really, that should be a new, separate question.
Assuming you are actually trying to run vlc, and guessing some other things, too, perhaps you actually want
subprocess.call(['vlc','{}/{}.{}'.format(moviefolder, formatted_title, title[1]),
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
If your PATH is correct, you should not need to specify /usr/local/bin/ explicitly (and if your PATH is wrong, correct it in the code before, instead of hardcoding a directory for the executable you want to call).
/usr/local/bin is a directory. You can't run a directory as if it were a command.
Anyhow, there's no point to having /usr/local/bin anywhere in your command at all. Leave out the shell=True, and explicitly call vlc:
subprocess.call([
'vlc',
'{}/{}.{}'.format(moviefolder, formatted_title, title[1])
])
When shell=True is used in subprocess.call, if the command arguments is a sequence, then the first element of the sequence needs to be the command, and the rest are treated as argument(s) to the shell itself.
So, this should do:
subprocess.call(["/usr/local/bin/{}".format(command), '-i','-c'], shell=True, ...)
Otherwise, you can make the command a string.
Example:
In [20]: subprocess.call(["cat spamegg", "-i", "-c"], shell=True)
foobar

Facing errors when reading huge text in python

Using Python3 my requirement is to read email files from a directory and filter Html tags in it.
I have managed to do it to a large extent.When I try to read the content of my output, it gives an error
for line in output.splitlines():
AttributeError: 'int' object has no attribute 'splitlines'
for file in glob.glob('spam/*.*'):
output = os.system("python html2txt.py " + file)
for line in output.splitlines():
print(line)
When I print output, it shows a filtered text.Any help is appreciated.
Try this as a replacement for the code you've provided:
import glob
files = glob.glob('spam/*.*')
for f in files:
with open(f) as spam_file:
for line in spam_file:
print(line)
If the files are indeed html files, I would recommend looking into BeautifulSoup.
The return value of os.system(command) is system-dependent, it supposes to return the (encoded) process exit value which represented by an int. read more here
On Unix, the return value is the exit status of the process encoded in
the format specified for wait(). Note that POSIX does not specify the
meaning of the return value of the C system() function, so the return
value of the Python function is system-dependent.
On Windows, the return value is that returned by the system shell
after running command, given by the Windows environment variable
COMSPEC: on command.com systems (Windows 95, 98 and ME) this is always
0; on cmd.exe systems (Windows NT, 2000 and XP) this is the exit
status of the command run; on systems using a non-native shell,
consult your shell documentation.
But in no system it returns a str and the method splitlines() is a str method. read more here
You are calling a str method on a int that is why you get the error:
AttributeError: 'int' object has no attribute 'splitlines'
On Unix, the return value is the exit status of the process encoded in
the format specified for wait(). Note that POSIX does not specify the
meaning of the return value of the C system() function, so the return
value of the Python function is system-dependent.
On Windows, the return value is that returned by the system shell
after running command. The shell is given by the Windows environment
variable COMSPEC: it is usually cmd.exe, which returns the exit status
of the command run; on systems using a non-native shell, consult your
shell documentation.
python docs
So your output variable is a integer not the result of the file being parsed by the
html2txt.py script.
And why do you run another python script outside of your current process ? Can't you just import whatever class of function that is doing the job from that module ?
Also there is an email module that can help you

Python Subprocess Error in using "cp"

I was trying to use subprocess calls to perform a copy operation (code below):
import subprocess
pr1 = subprocess.call(['cp','-r','./testdir1/*','./testdir2/'], shell = True)
and I got an error saying:
cp: missing file operand
Try `cp --help' for more information.
When I try with shell=False , I get
cp: cannot stat `./testdir1/*': No such file or directory
How do I get around this problem?
I'm using RedHat Linux GNOME Deskop version 2.16.0 and bash shell and Python 2.6
P.S. I read the question posted in Problems with issuing cp command with Popen in Python, and it suggested using shell = True option, which is not working for me as I mentioned :(
When using shell=True, pass a string, not a list to subprocess.call:
subprocess.call('cp -r ./testdir1/* ./testdir2/', shell=True)
The docs say:
On Unix with shell=True, the shell defaults to /bin/sh. If args is a
string, the string specifies the command to execute through the shell.
This means that the string must be formatted exactly as it would be
when typed at the shell prompt. This includes, for example, quoting or
backslash escaping filenames with spaces in them. If args is a
sequence, the first item specifies the command string, and any
additional items will be treated as additional arguments to the shell
itself.
So (on Unix), when a list is passed to subprocess.Popen (or subprocess.call), the first element of the list is interpreted as the command, all the other elements in the list are interpreted as arguments for the shell. Since in your case you do not need to pass arguments to the shell, you can just pass a string as the first argument.
This is an old thread now, but I was just having the same problem.
The problem you were having with this call:
subprocess.call(['cp','-r','./testdir1/*','./testdir2/'], shell = False)
was that each of the parameters after the first one are quoted. So to the shell sees the command like this:
cp '-r' './testdir1/*' './testdir2/'
The problem with that is the wildcard character (*). The filesystem looks for a file with the literal name '*' in the testdir1 directory, which of course, is not there.
The solution is to make the call like the selected answer using the shell = True option and none of the parameters quoted.
I know that the option of shell=True may be tempting but it's always inadvisable due to security issues. Instead, you can use a combination of the subprocess and glob modules.
For Python 3.5 or higher:
import subprocess
import glob
subprocess.run(['cp', '-r'] + glob.glob('./testdir1/*') + ['./testdir2/'])
For Python 3.4 or lower:
import subprocess
import glob
subprocess.call(['cp', '-r'] + glob.glob('./testdir1/*') + ['./testdir2/'])

Calling a subprocess with mixed data type arguments in Python

I am a bit confused as to how to get this done.
What I need to do is call an external command, from within a Python script, that takes as input several arguments, and a file name.
Let's call the executable that I am calling "prog", the input file "file", so the command line (in Bash terminal) looks like this:
$ prog --{arg1} {arg2} < {file}
In the above {arg1} is a string, and {arg2} is an integer.
If I use the following:
#!/usr/bin/python
import subprocess as sbp
sbp.call(["prog","--{arg1}","{arg2}","<","{file}"])
The result is an error output from "prog", where it claims that the input is missing {arg2}
The following produces an interesting error:
#!/usr/bin/python
import subprocess as sbp
sbp.call(["prog","--{arg1} {arg2} < {file}"])
all the spaces seem to have been removed from the second string, and equal sign appended at the very end:
command not found --{arg1}{arg2}<{file}=
None of this behavior seems to make any sense to me, and there isn't much that one can go by from the Python man pages found online. Please note that replacing sbp.call with sbp.Popen does not fix the problem.
The issue is that < {file} isn’t actually an argument to the program, but is syntax for the shell to set up redirection. You can tell Python to use the shell, or you can setup the redirection yourself.
from subprocess import *
# have shell interpret redirection
check_call('wc -l < /etc/hosts', shell=True)
# set up redirection in Python
with open('/etc/hosts', 'r') as f:
check_call(['wc', '-l'], stdin=f.fileno())
The advantage of the first method is that it’s faster and easier to type. There are a lot of disadvantages, though: it’s potentially slower since you’re launching a shell; it’s potentially non-portable because it depends on the operating system shell’s syntax; and it can easily break when there are spaces or other special characters in filenames.
So the second method is preferred.

Categories

Resources