diff --git a/src/io.cpp b/src/io.cpp index aa7e31c59..63565bae1 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -236,8 +237,32 @@ bool io_chain_t::append_from_specs(const redirection_spec_list_t &specs, const w if ((oflags & O_EXCL) && (errno == EEXIST)) { FLOGF(warning, NOCLOB_ERROR, spec.target.c_str()); } else { - FLOGF(warning, FILE_ERROR, spec.target.c_str()); - if (should_flog(warning)) wperror(L"open"); + if (should_flog(warning)) { + FLOGF(warning, FILE_ERROR, spec.target.c_str()); + auto err = errno; + // If the error is that the file doesn't exist + // or there's a non-directory component, + // find the first problematic component for a better message. + if (err == ENOENT || err == ENOTDIR) { + auto dname = spec.target; + struct stat buf; + + while (!dname.empty()) { + auto next = wdirname(dname); + if (!wstat(next, &buf)) { + if (!S_ISDIR(buf.st_mode)) { + FLOGF(warning, _(L"Path '%ls' is not a directory"), next.c_str()); + } else { + FLOGF(warning, _(L"Path '%ls' does not exist"), dname.c_str()); + } + break; + } + dname = next; + } + } else { + wperror(L"open"); + } + } } // If opening a file fails, insert a closed FD instead of the file redirection // and return false. This lets execution potentially recover and at least gives diff --git a/tests/checks/exec.fish b/tests/checks/exec.fish index 04823fb69..ddaa203a6 100644 --- a/tests/checks/exec.fish +++ b/tests/checks/exec.fish @@ -1,12 +1,12 @@ #RUN: %fish %s exec cat /not/a/valid/path echo $pipestatus : $status #CHECK: 1 : 1 #CHECKERR: warning: An error occurred while redirecting file '/not/a/valid/path' -#CHECKERR: open: No such file or directory +#CHECKERR: warning: Path '/not' does not exist # Here the first process will launch, the second one will not. command true | command true | command true > /not/a/valid/path echo $pipestatus : $status #CHECK: 0 0 1 : 1 #CHECKERR: warning: An error occurred while redirecting file '/not/a/valid/path' -#CHECKERR: open: No such file or directory +#CHECKERR: warning: Path '/not' does not exist # Pipeline breaks do not result in dangling jobs. command true | command cat > /not/a/valid/path ; jobs #CHECKERR: warning: An error occurred while redirecting file '/not/a/valid/path' -#CHECKERR: open: No such file or directory +#CHECKERR: warning: Path '/not' does not exist #CHECK: jobs: There are no jobs # Regression test for #7038 cat /dev/zero | dd > /not/a/valid/path echo 'Not hung' #CHECKERR: warning: An error occurred while redirecting file '/not/a/valid/path' -#CHECKERR: open: No such file or directory +#CHECKERR: warning: Path '/not' does not exist #CHECK: Not hung diff --git a/tests/checks/redirect.fish b/tests/checks/redirect.fish index 8c62783fe..4043e6d91 100644 --- a/tests/checks/redirect.fish +++ b/tests/checks/redirect.fish @@ -138,3 +138,7 @@ echo "/bin/echo pipe 12 <&12 12<&-" | source 12<&0 #CHECK: pipe 10 #CHECK: pipe 11 #CHECK: pipe 12 + +echo foo >/bin/echo/file +#CHECKERR: warning: An error occurred while redirecting file '/bin/echo/file' +#CHECKERR: warning: Path '/bin/echo' is not a directory diff --git a/tests/pexpects/pipeline.py b/tests/pexpects/pipeline.py index 129bc2411..47aa3f199 100644 --- a/tests/pexpects/pipeline.py +++ b/tests/pexpects/pipeline.py @@ -37,6 +37,6 @@ expect_prompt("jobs: There are no jobs", unmatched="Should be no jobs") # Check that this weird invalid double-redirection doesn't crash fish. sendline("cat | cat