diff --git a/easybuild/tools/run.py b/easybuild/tools/run.py index 92c92a1b41..6a568a9f70 100644 --- a/easybuild/tools/run.py +++ b/easybuild/tools/run.py @@ -42,6 +42,7 @@ import re import signal import shutil +import string import subprocess import sys import tempfile @@ -180,6 +181,19 @@ def cache_aware_func(cmd, *args, **kwargs): run_shell_cmd_cache = run_cmd_cache +def fileprefix_from_cmd(cmd, allowed_chars=False): + """ + Simplify the cmd to only the allowed_chars we want in a filename + + :param cmd: the cmd (string) + :param allowed_chars: characters allowed in filename (defaults to string.ascii_letters + string.digits + "_-") + """ + if not allowed_chars: + allowed_chars = f"{string.ascii_letters}{string.digits}_-" + + return ''.join([c for c in cmd if c in allowed_chars]) + + @run_shell_cmd_cache def run_shell_cmd(cmd, fail_on_error=True, split_stderr=False, stdin=None, env=None, hidden=False, in_dry_run=False, verbose_dry_run=False, work_dir=None, use_bash=True, @@ -233,7 +247,6 @@ def to_cmd_str(cmd): work_dir = os.getcwd() cmd_str = to_cmd_str(cmd) - cmd_name = os.path.basename(cmd_str.split(' ')[0]) # auto-enable streaming of command output under --logtostdout/-l, unless it was disabled explicitely if stream_output is None and build_option('logtostdout'): @@ -244,6 +257,7 @@ def to_cmd_str(cmd): if output_file: toptmpdir = os.path.join(tempfile.gettempdir(), 'run-shell-cmd-output') os.makedirs(toptmpdir, exist_ok=True) + cmd_name = fileprefix_from_cmd(os.path.basename(cmd_str.split(' ')[0])) tmpdir = tempfile.mkdtemp(dir=toptmpdir, prefix=f'{cmd_name}-') cmd_out_fp = os.path.join(tmpdir, 'out.txt') _log.info(f'run_cmd: Output of "{cmd_str}" will be logged to {cmd_out_fp}') diff --git a/test/framework/run.py b/test/framework/run.py index db74940aec..e63e4e219c 100644 --- a/test/framework/run.py +++ b/test/framework/run.py @@ -35,6 +35,7 @@ import os import re import signal +import string import stat import subprocess import sys @@ -51,7 +52,7 @@ from easybuild.tools.config import update_build_option from easybuild.tools.filetools import adjust_permissions, change_dir, mkdir, read_file, write_file from easybuild.tools.run import RunShellCmdResult, RunShellCmdError, check_async_cmd, check_log_for_errors -from easybuild.tools.run import complete_cmd, get_output_from_process, parse_log_for_error +from easybuild.tools.run import complete_cmd, fileprefix_from_cmd, get_output_from_process, parse_log_for_error from easybuild.tools.run import run_cmd, run_cmd_qa, run_shell_cmd, subprocess_terminate from easybuild.tools.config import ERROR, IGNORE, WARN @@ -193,6 +194,26 @@ def test_run_shell_cmd_basic(self): self.assertTrue(isinstance(res.output, str)) self.assertTrue(res.work_dir and isinstance(res.work_dir, str)) + def test_fileprefix_from_cmd(self): + """test simplifications from fileprefix_from_cmd.""" + cmds = { + 'abd123': 'abd123', + 'ab"a': 'aba', + 'a{:$:S@"a': 'aSa', + 'cmd-with-dash': 'cmd-with-dash', + 'cmd_with_underscore': 'cmd_with_underscore', + } + for cmd, expected_simplification in cmds.items(): + self.assertEqual(fileprefix_from_cmd(cmd), expected_simplification) + + cmds = { + 'abd123': 'abd', + 'ab"a': 'aba', + '0a{:$:2@"a': 'aa', + } + for cmd, expected_simplification in cmds.items(): + self.assertEqual(fileprefix_from_cmd(cmd, allowed_chars=string.ascii_letters), expected_simplification) + def test_run_cmd_log(self): """Test logging of executed commands.""" fd, logfile = tempfile.mkstemp(suffix='.log', prefix='eb-test-')