Skip to content

head: fix -c0 and -n0 on directories to match GNU behavior#12217

Merged
cakebaker merged 1 commit into
mainfrom
fix/head-c0-directory
May 12, 2026
Merged

head: fix -c0 and -n0 on directories to match GNU behavior#12217
cakebaker merged 1 commit into
mainfrom
fix/head-c0-directory

Conversation

@sylvestre
Copy link
Copy Markdown
Contributor

When zero bytes or zero lines are requested, there is nothing to read, so we should not check whether the path is a directory. GNU head succeeds in this case because it never attempts to open/read the file.

Previously, uutils head would stat the file and reject directories even when no reading was needed, causing a spurious 'Is a directory' error.

Fixes #12215

Changes

  • Skip the is_dir() check when -c0 or -n0 is specified
  • Added two integration tests: test_zero_bytes_on_directory_succeeds and test_zero_lines_on_directory_succeeds

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 10, 2026

GNU testsuite comparison:

Skip an intermittent issue tests/rm/isatty (fails in this run but passes in the 'main' branch)
Skip an intermittent issue tests/tail/retry (fails in this run but passes in the 'main' branch)
Skipping an intermittent issue tests/tail/tail-n0f (passes in this run but fails in the 'main' branch)
Congrats! The gnu test tests/printf/printf-surprise is now passing!
Congrats! The gnu test tests/seq/seq-epipe is now passing!
Note: The gnu test tests/misc/write-errors was skipped on 'main' but is now failing.

@sylvestre sylvestre force-pushed the fix/head-c0-directory branch from 191d756 to 4030cc2 Compare May 10, 2026 10:24
@cakebaker
Copy link
Copy Markdown
Contributor

I don't know if you have seen it: the new tests fail on Windows :|

@sylvestre sylvestre force-pushed the fix/head-c0-directory branch from 4030cc2 to 1bed4e9 Compare May 11, 2026 11:43
Comment thread tests/by-util/test_head.rs Outdated
Comment on lines +944 to +945
.no_stdout()
.no_stderr();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A detail:

Suggested change
.no_stdout()
.no_stderr();
.no_output();

Comment thread tests/by-util/test_head.rs Outdated
Comment on lines +954 to +955
.no_stdout()
.no_stderr();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here:

Suggested change
.no_stdout()
.no_stderr();
.no_output();

Comment thread src/uu/head/src/head.rs Outdated
// fails with "Permission denied").
let zero_output = matches!(options.mode, Mode::FirstBytes(0) | Mode::FirstLines(0));
let is_dir = Path::new(file).is_dir();
if is_dir && !zero_output {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move the !zero_output check into the block. Something like:

if is_dir {
    if !zero_output {
        // here comes the show! call
    }
    continue;
}

It allows you to get rid of the ifs on line 464 and 485.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, this is not correct, at least if we want the same behavior as GNU head :| If there are multiple directories and a non-zero input, GNU head shows the directories on stdout whereas we don't:

$ head -c 1 . .
==> . <==
head: error reading '.': Is a directory

==> . <==
head: error reading '.': Is a directory
$ cargo run -q head -c 1 . .
head: error reading '.': Is a directory
head: error reading '.': Is a directory

Comment thread src/uu/head/src/head.rs Outdated
1,
translate!("head-error-reading-file", "name" => file.quote(), "err" => "Is a directory")
));
continue;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this continue is incorrect, see my previous comment.

Suggested change
continue;

When zero bytes or zero lines are requested, there is nothing to read,
so we should not check whether the path is a directory. GNU head
succeeds in this case because it never attempts to open/read the file.

Previously, uutils head would stat the file and reject directories
even when no reading was needed, causing a spurious 'Is a directory'
error.

Fixes #12215
@sylvestre sylvestre force-pushed the fix/head-c0-directory branch from 1bed4e9 to 82ce536 Compare May 11, 2026 20:59
@cakebaker cakebaker merged commit 28cff68 into main May 12, 2026
167 of 168 checks passed
@cakebaker cakebaker deleted the fix/head-c0-directory branch May 12, 2026 12:49
@collinfunk
Copy link
Copy Markdown

It is a bit strange to write error reading when read is never called, no?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

head: behaviour with -c0 differs from upstream

3 participants