tl; dr : see. Conclusions below
To understand where the empty file name comes from (to the left of : in the error message):
>>> import os >>> os.system('./hi.py') : No such file or directory 32512
You can invoke the command directly without a shell (such as bash):
>>> from subprocess import Popen, PIPE >>> Popen('./hi.py', stdout=PIPE, stderr=PIPE).communicate() (b'', b'/usr/bin/env: python\r: No such file or directory\n')
the result says that b'\r' recognized as part of the command name. On POSIX systems, the file name can be an arbitrary sequence of bytes (except zero b'\0' and slash b'/' ). If you create an executable file in the PATH, which is named b'python\r' :
>>> os.symlink('/usr/bin/python', os.path.expanduser('~/bin/python\r'))
then the error disappears:
>>> os.system('./hi.py') hi! 0
where hi.py simple Python executable script with Windows endings of strings:
>>> open('hi.py', 'wb').write(b'#!/usr/bin/env python\r\nprint("hi!")') >>> os.chmod('hi.py', 0b111101101) # rwxr-xr-x
The command name is not visible in the first example, because b'\r' in the terminal moves the cursor to the beginning of the line:
>>> print('abc\r12') 12c
that is, the letters ab are clogged with 12 in this example. The example demonstrates the traditional behavior for CR (carriage return).
Python itself supports the universal mode of new lines and therefore works with '\r\n' and on Unix:
>>> os.unlink(os.path.expanduser('~/bin/python\r')) >>> os.system('python ./hi.py') hi! 0
#! -string (shebang) does not recognize python , and not even bash , but the kernel of the operating system (Linux, for example). By default, modern versions of Unix ignore the horizontal space (space b' ' and tab b'\t' ) at the end of #! -strings:
>>> import string >>> for ws in string.whitespace: ... with open('space[%X].py' % ord(ws), 'w') as f: ... f.write('#!/usr/bin/env python%s\nprint("%X")' % (ws, ord(ws))) >>> from glob import glob >>> for cmd in glob('./spac*.py'): ... os.chmod(cmd, 0b111101101) ... Popen(cmd, stdout=PIPE, stderr=PIPE).communicate() ... (b'', b'/usr/bin/env: python\r: No such file or directory\n') (b'20\n', b'') (b'A\n', b'') (b'', b'/usr/bin/env: python\x0b: No such file or directory\n') (b'9\n', b'') (b'', b'/usr/bin/env: python\x0c: No such file or directory\n')
That is, the command works for ' \n\t' and does not work for '\r\v\f' .
If you want to run the script directly (without explicitly specifying the interpreter), then you should use Unix line endings, as suggested by @alexander barakin :
>>> data = open('./hi.py', 'rb').read() >>> open('./hi.py', 'wb').write(data.replace(b'\r\n', b'\n')) 34 >>> os.system('./hi.py') hi! 0
Version control systems such as Mercurial (hg), git probably have options that will help replace the new lines of the current operating system with \n for .py files automatically if you cannot use an editor that supports \n directly .
If the author of the command implemented on Python works only on Windows and does not want to complicate the process, then by creating setup.py , which defines setuptools' entry_points , you can automatically support different systems (if there are no other incompatibilities) :
from setuptools import setup setup(name='program', version='1.2.5', description='', long_description='', author='', author_email='', url='', license='', py_modules=['program'], entry_points={ 'console_scripts': ['program=program:main'] } )
where the program.py file, for example, could be created on Windows:
>>> open('program.py', 'wb').write(b'#!/usr/bin/env python\r\ndef main():\r\n print("hi!")\r\n') 52
The installation creates scripts with the correct #! - string on Unix systems and corresponding .exe files on Windows:
>>> import subprocess >>> import sys >>> subprocess.call([sys.executable] + '-m pip install -e .'.split()) Obtaining file:///tmp/prj Installing collected packages: program Running setup.py develop for program Successfully installed program-1.2.5 0 >>> subprocess.call('program') hi! 0
Direct reading of a machine-generated file confirms that '\n' used on Unix ( docker run -v $PWD:/tmp/prj -it --rm python ) system:
>>> import shutil >>> open(shutil.which('program'), 'rb').readline() b'#!/usr/local/bin/python3\n'
findings
'\r' in #! -string is considered as part of the command name ( b'python\r' ) on some systems, which causes the error: "No such file or directory" when trying to run a script
some terminals, encountering '\r' in the output of the program, move the cursor to the beginning of the line, so the file name in the error message (the part to the left of the colon) seems empty (the message text is printed on top of it)
it is not clear why the Linux kernel does not ignore '\r' at the end of #! -strings, as it does for spaces and tabs. Perhaps this choice has been made to help notice that the script has incompatible line terminations (which may indicate other problems with portability in the program)
The preferred way to work around this problem is to use an editor that supports '\n' on Windows. Or, if development is carried out exclusively on Windows, then the creation of setup.py with setuptools' entry_points as shown above, so that during the installation, the correct wrapper scripts for the platform are generated automatically.
\r\n). maybe it’s worth replacing line feeds with “unix”\n? This can be done, for example, using the$ dos2unix файл. - aleksandr barakin