Repository: dtrx-py/dtrx (language: python)

Issue:
Crash with file not found even though the file does exist
Using dtrx with any (or no) options crashes with the following stacktrace:

```
dtrx hello.zip
Traceback (most recent call last):
  File "/usr/local/bin/dtrx", line 1404, in <module>
    sys.exit(app.run())
  File "/usr/local/bin/dtrx", line 1388, in run
    self.try_extractors(filename,
  File "/usr/local/bin/dtrx", line 1335, in try_extractors
    for extractor in builder:
  File "/usr/local/bin/dtrx", line 1054, in get_extractor
    getattr(self, 'try_by_' + func_name)(self.filename)
  File "/usr/local/bin/dtrx", line 1081, in try_by_magic
    process = subprocess.Popen(['file', '-zL', filename],
  File "/usr/local/lib/python3.8/subprocess.py", line 858, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/usr/local/lib/python3.8/subprocess.py", line 1704, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'file'
~# command terminated with exit code 137
```
The file exist and can even be uncompressed using `unzip`.

Using dtrx-noahp-7.1.2


Discussion / hints:
Seems to be OS related.

I was using this docker image for python :
```
FROM python:3.10-slim-buster
```
It resulted in the above stacktrace.

I ended up using this docker image:
```
FROM python:3.9-alpine3.13
```
It's working...
Ah yes, dtrx will use the `file` program when detecting the archive type, so if that's not installed on the host it will fail there. We could definitely do better, two things come to mind-

- detect missing `file` program and gracefully report + exit
- continue with best effort extract when it's missing (eg Windows may not have it available)

I'll update here if I make those improvements.

Thanks for reporting this issue!

Fix patch:
diff --git a/dtrx/dtrx.py b/dtrx/dtrx.py
index 0e6ba8b..ab25e8b 100755
--- a/dtrx/dtrx.py
+++ b/dtrx/dtrx.py
@@ -1340,9 +1340,15 @@ def magic_map_matches(cls, output, magic_map):
     magic_map_matches = classmethod(magic_map_matches)
 
     def try_by_magic(cls, filename):
-        process = subprocess.Popen(["file", "-zL", filename], stdout=subprocess.PIPE)
-        status = process.wait()
-        if status != 0:
+        try:
+            process = subprocess.Popen(
+                ["file", "-zL", filename], stdout=subprocess.PIPE
+            )
+            status = process.wait()
+            if status != 0:
+                return []
+        except FileNotFoundError:
+            logger.error("'file' command not found, skipping magic test")
             return []
         output = process.stdout.readline().decode("ascii")
         process.stdout.close()


Test patch:
diff --git a/test.sh b/test.sh
index c4e930f..f029507 100755
--- a/test.sh
+++ b/test.sh
@@ -26,6 +26,8 @@ case $RUN_JOB in
         # script writes files to an in-tree location, so run serially to avoid
         # clobbering during the tests
         docker run --rm -v "$(pwd)":/mnt/workspace -t "$DOCKER_IMAGE_NAME" bash -c "tox $TOX_ARGS"
+
+        ./tools/test-nonexistent-file-cmd.sh
     ;;
     rst2man)
         # build man page from README
diff --git a/tools/test-nonexistent-file-cmd.sh b/tools/test-nonexistent-file-cmd.sh
new file mode 100755
index 0000000..0e3ddd7
--- /dev/null
+++ b/tools/test-nonexistent-file-cmd.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+set -ex
+
+# Run a quick test in a docker image that doesn't have the 'file' command
+# installed, and check for any unhandled tracebacks in the output.
+docker run --rm -t --volume "$PWD:/workdir" python:3.10-slim-buster@sha256:ae30f2166f4389e553b642af9f22f7d17ba7fe584e0dd8bf9c75c36d836aabc3 /bin/bash -c "
+cd /workdir && pip install . && touch /tmp/yolo.zip && cd /tmp && dtrx yolo.zip 2>&1 | tee /dev/stderr | ( ! grep 'Traceback (most recent call last):')
+"