Sublime Forum

Python error when attempting build with shell_cmd

#1

I have created a build script to test sql with psql. When the script is invoked I see the following in the console.

Here is the JSON for the file, sql.sublime-build

`{
"shell_cmd": ["psql", "-U", "tevs", "-d", "tevs", "-o",   "/Users/Wes/Dropbox/Programming/ElectionTransparency/psql_out.txt", "-f", "$file"]
}`

When I run it I see the following on the console
Traceback (most recent call last): File "/Applications/Sublime Text.app/Contents/MacOS/sublime_plugin.py", line 795, in run_ return self.run(**args) File "exec in /Applications/Sublime Text.app/Contents/MacOS/Packages/Default.sublime-package", line 238, in run TypeError: Can't convert 'list' object to str implicitly
If I insert a print call in sublime_plugin.py just before line 795 I the following
{'shell_cmd': ['psql', '-U', 'tevs', '-d', 'tevs', '-o', '/Users/Wes/Dropbox/Programming/ElectionTransparency/psql_out.txt', '-f', '/Users/Wes/Dropbox/Programming/ElectionTransparency/testbuild.sql']}

Can this be corrected?

0 Likes

#2

shell_cmd takes a single string as its value but cmd takes an array of strings.

TypeError: Can’t convert ‘list’ object to str implicitly

This error is telling you that the command is expecting a string, but instead it got a list (array) and it doesn’t know how to convert it on its own.

So basically, just stick all of the strings together into one:

{
    "shell_cmd": "psql -U tevs -d tevs -o /Users/Wes/Dropbox/Programming/ElectionTransparency/psql_out.txt -f $file"
}
0 Likes

#3

Then cmd‘s better, no? it’ll automatically quote/escape the variables’ value ($file for example), no? Or it always does it?

0 Likes

#4

They have different use cases. If you just want to launch a single executable with parameters, use cmd. If you want to use any shell features (such as piping, shell commands or running two commands consecutively), use shell_cmd, which will let the shell handle your command string (after ST has replaced its variables).

On Windows, running an executable without its extension (e.g. python instead of python.exe) also requires its “shell”.

1 Like

#5

Yes and No; It depends on what you’re trying to do.

Build systems use the exec command to execute the task for building unless you override that with a target option in the build system itself. The exec command behaves in (basically) two ways:

If you use cmd, you pass it an array of strings where the first one is the task to execute and all of the rest of the elements are the arguments to pass to the command, in order.

Behind the scenes, exec uses subprocess.Popen and passes this list to that call directly, which causes it (via a call to execvp if you’re familiar with that) to execute exactly what you gave it. Since you’re passing it an explicit list of arguments, you get things like quoting of e.g. spaces in filenames for free (although technically it’s less quoting and more that the arguments are passed directly).

On the other hand, if you use shell_cmd, you pass it a single string that is the complete command line. Behind the scenes, this is handled by (on Linux/MacOS) as if you tried to execute the command by passing it as an argument to bash. In effect, exec converts this into a call of something like:

"cmd": ["/bin/bash", "-c", "shell_cmd_string_you_provided"]

Since this gets invoked by bash, you need to be careful to put your own quotes around things like $file that might expand into a filename with spaces, because now it’s the shell parsing the command instead of you specifically saying “and the third argument is this”.

What this gains you is the ability to do things like I/O pipes and redirecting, or chaining multiple commands together which you otherwise cannot do.

This is why (for example) the build system for C++ includes the following:

"variants":
[
    {
        "name": "Run",
        "shell_cmd": "g++ \"${file}\" -o \"${file_path}/${file_base_name}\" && \"${file_path}/${file_base_name}\""
    }
]
2 Likes