In a script called generator.py i have this line of code:
arg=''
for i,a in enumerate(sys.argv):
if(i!=0):
arg+=a+" "
if i print arg i can see the shell argument minus the script's name
And it was fine but if a use a regex line *.h i discovered that the interpreter resolve this rule for me and dispite *.h printing a i can see:
file.h file2.h .... filen.h
My question is how can i get the non resolved command line string?
My guess is that you probably can't catch it - the arguments to script get resolved on an os level before they are passed to sys.argv.
You're calling the script from command line, which resolves the glob for you and passes the result to the python script.
If you don't want the glob resolved by the command line, just escape the wildcard *.
Your shell is expanding the * to its meaning of wildcard.
Possible solutions are passing "*.h", '*.h' or \*.h from the command line to your script, which will receive the exact *.h string.
As #SpoonMeiser suggested, you can check what your script receives by using this:
echo script.py arg1 arg2 blahblah
This will print back what is effectively called after the expansion of the glob and give you an idea of what happened.
Also thanks to #SpoonMeiser for giving the concept of glob for this case, that you can look more in detail here
Related
I have to use:
x=input()
subprocess.Popen(f'PowerShell -Executionpolicy byPass {x}\n')
To open an executable, but it does not allow me to use a path from the input of an variable that contains backslash or spaces that are not written in the following way:
'\\' for backslashes.
'\u0020' for spaces.
For C:\Users\Administrator\Desktop\Folder Name\executable.exe
it should look like this:
'PowerShell -Executionpolicy byPass C:\\Users\\Administrator\\Desktop\\Folder\u0020Name\\executable.exe'
How could I replace all those spaces and backslashes on the variable for those unicode equivalents?
I need the user to be able just to copy and paste the executable's path.
I tried this, but didn't work:
x=input()
x=x.replace('\','\\')
x=x.replace(' ','\u0020')
I'm using Python 3.
Do not try to use escaping on the Python side: \, when provided as part of a variable value needs no escaping, and neither do spaces.
For PowerShell's sake, however, file paths that contain spaces require quoting; when using PowerShell's CLI, double-quoting ("...") is needed.
Additionally, you use the -File CLI parameter explicitly in order to execute a script file, because, by default, the -Command parameter is implied in Windows PowerShell, which interprets the following argument(s) as PowerShell source code, after stripping unescaped " from them, which causes script file paths with spaces to break.
Note: This applies to Windows PowerShell, but is no longer strictly necessary in PowerShell (Core) 7+, which now defaults to -File rather than -Command.
Therefore:
import subprocess
print('Enter the path of the .ps1 script to execute:')
x=input()
subprocess.Popen(f'PowerShell -Executionpolicy ByPass -File "{x}"').wait()
Note: To avoid unnecessary processing and to ensure a more predictable runtime environment, consider preceding -File with the -NoProfile switch, which suppresses loading of PowerShell's profile files
See also:
Guidance on when to use -File vs. -Command
A comprehensive overview of PowerShell's CLI, in both editions.
As roeland's answer demonstrates, you may alternatively pass the arguments that make up the PowerShell CLI command line individually to subprocess.Popen(), as elements of an array.
While Python then conveniently takes care of double-quoting the elements if necessary on the command line it synthesizes behind the scenes (on Windows), the same rules discussed above apply here too: the script-file path argument must be qualified with -File in order for script paths with spaces to be invoked correctly:
subprocess.Popen(['PowerShell', '-Executionpolicy', 'ByPass', '-File', x]).wait()
The easiest way to pass arguments to a subprocess is to pass in the program with arguments as a list:
subprocess.Popen(['PowerShell', '-Executionpolicy', 'byPass', x])
This assumes x will only contain one single extra argument. If it potentially contains multiple arguments you’ll need to make a decision on how to split it, and how to handle paths with spaces.
I want to use metaflac (https://linux.die.net/man/1/metaflac) command from within a python script.
from subprocess import run
flac_files = "/home/fricadelle/Artist - Album (2008)/*.flac"
run(['metaflac', '--add-replay-gain', flac_files])
I get
The FLAC file could not be opened. Most likely the file does not exist
or is not readable.
if I add shell = True to the run function I'd get:
ERROR: you must specify at least one FLAC file;
metaflac cannot be used as a pipe
So what do I do wrong? Thanks!
PS: of course the command works fine in a shell:
metaflac --add-replay-gain /home/fricadelle/Artist\ -\ Album \(2008\)/*.flac
Unless you specify shell=True (and as a first approximation, you should never specify shell=True), the arguments you provide are passed as is, with no shell expansions, word-splitting or dequoting. So the filename you pass as an argument is precisely /home/fricadelle/Artist - Album (2008)/*.flac, which is not the name of any file. (That's why you don't need to add backslashes before the spaces and parentheses. If you specified shell=True -- and I repeat, you really should avoid that -- then you would need to include backslashes so that the shell doesn't split the name into several different words.)
When you type
flac_files = "/home/fricadelle/Artist - Album (2008)/*.flac unquoted in a shell, the shell will try to expand that to a list of all the files whose names match then pattern, and will then pass that list as separate arguments. Since subprocess.run doesn't do this, you will have to do it yourself, which you would normally do with glob.glob. For example,
from subprocess import run
from glob import glob
flac_files = "/home/fricadelle/Artist - Album (2008)/*.flac"
run(['metaflac', '--add-replay-gain'] + glob(flac_files))
Note: unlike the shell, glob.glob will return an empty list if the pattern matches no files. You really should check for this error rather than invoke metaflac with no filename options.
See the answer here for a better explanation.
Globbing doesn't work the way you're expecting it to here, you need to specify shell=True, but then you'll need to drop the list.
run('metaflac --add-replay-gain ' + flac_files, shell=True)
Should do the trick.
the string that contains a file looks like this in the console:
>>> target_file
'src//data//annual_filings//ABB Ltd//ABB_ar_2015.pdf'
I got the target_file from a call to os.walk
The goal is to build a command to run in subprocess.call
Something like:
from subprocess import call
cmd_ = r'qpdf-7.0.0/bin/qpdf --password=%s --decrypt %s %s' %('', target_file, target_file)
call([cmd_])
I tried different variations, setting shell to either True or False.
Replacing the // with /,\ etc.
The issue seems to be with the space in the folder (I can not change the folder name).
The python code needs to run on Windows
you have to define cmd_ as a list of arguments not a list with a sole string in it, or subprocess interprets the string as the command (doesn't even try to split the args):
cmd_ = ['qpdf-7.0.0/bin/qpdf','--password=%s'%'','--decrypt',target_file, target_file]
call(cmd_)
and leave the quoting to subprocess
As a side note, no need to double the slashes. It works, but that's unnecessary.
First of all, I am new to programming.
To run python code in an external shell window, I followed the instructions given on this page
link
My problem is that if I save the python file in any path that contains a folder name with a space, it gives me this error:
C:\Python34\python.exe: can't open file 'C:\Program': [Errno 2] No such file or directory
Does not work:
C:\Program Files\Python Code
Works:
C:\ProgramFiles\PythonCode
could someone help me fix the problem???
Here is the code:
import sublime
import sublime_plugin
import subprocess
class PythonRunCommand(sublime_plugin.WindowCommand):
def run(self):
command = 'cmd /k "C:\Python34\python.exe" %s' % sublime.active_window().active_view().file_name()
subprocess.Popen(command)
subprocess methods accept a string or a list. Passing as a string is the lazy way: just copy/paste your command line and it works. That is for hardcoded commands, but things get complicated when you introduce parameters known at run-time only, which may contain spaces, etc...
Passing a list is better because you don't need to compose your command and escape spaces by yourself. Pass the parameters as a list so it's done automatically and better that you could do:
command = ['cmd','/k',r"C:\Python34\python.exe",sublime.active_window().active_view().file_name()]
And always use raw strings (r prefix) when passing literal windows paths or you may have some surprises with escape sequences meaning something (linefeed, tab, unicode...)
In this particular case, if file associations are properly set, you only need to pass the python script without any other command prefix:
command = [sublime.active_window().active_view().file_name()]
(you'll need shell=True added to the subprocess command but it's worth it because it avoids to hardcode python path, and makes your plugin portable)
I'm trying to call 'sed' from Python and having troubles passing the command line via either subprocess.check_call() or os.system().
I'm on Windows 7, but using the 'sed' from Cygwin (it's in the path).
If I do this from the Cygwin shell, it works fine:
$ sed 's/ /\ /g' <"C:foobar" >"C:foobar.temp"
In Python, I've got the full pathname I'm working with in "name". I tried:
command = r"sed 's/ /\ /g' " + "<" '\"' + name + '\" >' '\"' + name + '.temp' + '\"'
subprocess.check_call(command, shell=True)
All the concatenation is there to make sure I have double quotes around the input and output filenames (in case there are blank spaces in the Windows file path).
I also tried it replacing the last line with:
os.system(command)
Either way, I get this error:
sed: -e expression #1, char 2: unterminated `s' command
'amp' is not recognized as an internal or external command,
operable program or batch file.
'nbsp' is not recognized as an internal or external command,
operable program or batch file.
Yet, as I said, it works OK from the console. What am I doing wrong?
The shell used by subprocess is probably not the shell you want. You can specify the shell with executable='path/to/executable'. Different shells have different quoting rules.
Even better might be to skip subprocess altogether, and write this as pure Python:
with open("c:foobar") as f_in:
with open("c:foobar.temp", "w") as f_out:
for line in f_in:
f_out.write(line.replace(' ', ' '))
I agree with Ned Batchelder's assessment, but think what you might want to consider using the following code because it likely does what you ultimately want to accomplish which can be done easily with the help of Python's fileinput module:
import fileinput
f = fileinput.input('C:foobar', inplace=1)
for line in f:
line = line.replace(' ', ' ')
print line,
f.close()
print 'done'
This will effectively update the given file in place as use of the keyword suggests. There's also an optional backup= keyword -- not used above -- which will save a copy of the original file if desired.
BTW, a word of caution about using something like C:foobar to specify the file name because on Windows it means a file of that name in whatever the current directory is on drive C:, which might not be what you want.
I think you'll find that, in Windows Python, it's not actually using the CygWin shell to run your command, it's instead using cmd.exe.
And, cmd doesn't play well with single quotes the way bash does.
You only have to do the following to confirm that:
c:\pax> echo hello >hello.txt
c:\pax> type "hello.txt"
hello
c:\pax> type 'hello.txt'
The system cannot find the file specified.
I think the best idea would be to use Python itself to process the file. The Python language is a cross-platform one which is meant to remove all those platform-specific inconsistencies, such as the one you've just found.