The FileNotFoundError
happens because - in the absence of shell=True
- Python tries to find an executable whose file name is the entire string you are passing in. You need to add shell=True
to get the shell to parse and execute the string, or figure out how to rearticulate this command line to avoid requiring a shell.
As an aside, the shell programming here is decidedly weird. On any normal system, date
will absolutely never output "+tz+"
and so the rest of the processing is moot.
Further, using wc -w
to count the number of output words from grep
is unusual. The much more common use case (if you can't simply use grep -c
to count the number of matching lines) would be to use wc -l
to count lines of output from grep
.
Anyway, if you can, you want to avoid shell=True
; if the intent here is to test the date
command, you should probably replace the rest of the shell script with native Python code.
Pros:
- The person trying to understand the program only needs to understand Python, not shell script.
- The script will have fewer external dependencies (here,
date
) rather than require a Unix-like platform.
Cons:
- Reimplementing standard Unix tools in Python is tiresome and sometimes rather verbose.
With that out of the way, if the intent is simply to count how wany times "+tz+"
occurs in the output from date
, try
p = subprocess.run(['date'],
capture_output=True, text=True,
check=True)
result = len(p.stdout.split('"+tz+"'))-1
The keyword argument text=True
requires Python 3.7; for compatibility back to earlier Python versions, try the (misnomer) legacy synonym universal_newlines=True
. For really old Python versions, maybe fall back to subprocess.check_output()
.
If you really need the semantics of the -w
option of grep
, you need to check if the characters adjacent to the match are not alphabetic, and exclude those which are. I'm leaving that as an exercise, and in fact would assume that the original shell script implementation here was not actually correct. (Maybe try re.split(r'(?<=^|\W)"\+tz\+"(?=\W|$)', p.stdout)
.)
In more trivial cases (single command, no pipes, wildcards, redirection, shell builtins, etc) you can use Python's shlex.split()
to parse a command into a correctly quoted list of arguments. For example,
>>> import shlex
>>> shlex.split(r'''one "two three" four\ five 'six seven' eight"'"nine'"'ten''')
['one', 'two three', 'four five', 'six seven', 'eight\'nine"ten']
Notice how the regular string split()
is completely unsuitable here; it simply splits on every whitespace character, and doesn't support any sort of quoting or escaping. (But notice also how shlex.split
boneheadedly just returns a list of tokens from the original input:
>>> shlex.split('''date | grep -o -w '"+tz+"' | wc -w''')
['date', '|', 'grep', '-o', '-w', '"+tz+"', '|', 'wc', '-w']
(Even more parenthetically, this isn't exactly the original input, which had a superfluous extra single quote after '"+tz+"'
).
If you were to pass this to subprocess.run
, it is in fact passing |
and grep
etc as arguments to date
, not implementing a shell pipeline! You still have to understand what you are doing.)