17

find command can output names of files as a null-delimited strings (if -print0 is provided), and xargs can consume them with -0 option turned on. But in between, it's hard to manipulate that collection of files - sort command has -z switch, that makes it possible to sort those files, but head and tail don't have them.

How can I do head and tail on those null-delimited inputs in a convenient way? (I can always create a short & slow ruby script, but I hope that there could be a better way)

20

GNU head and tail since coreutils version 8.25 have a -z option for that.

With older versions or for non-GNU systems, you can try and swap \0 and \n:

find ... -print0 |
  tr '\0\n' '\n\0' |
  head |
  tr '\0\n' '\n\0'

Note that some head implementations can't cope with NUL characters (and they're not required to by POSIX), but where find supports -print0, head and text utilities generally support NUL characters.

You can also use a function to wrap any command between the two trs:

nul_terminated() {
  tr '\0\n' '\n\0' | "$@" | tr '\0\n' '\n\0'
}

find ... -print0 | nul_terminated tail -n 12 | xargs -r0 ...

Keep in mind that under nul_terminated, a \0 means a newline character. So for instance, to replace \n with _:

find . -depth -name $'*\n*' -print0 | nul_terminated sed '
  p;h;s,.*/,,;s/\x0/_/g;H;g;s,[^/]*\n,,' | xargs -r0n2 mv

(\x0 being also a GNU extension).

If you need to run more than one filtering command, you can do:

find ... -print0 |
  nul_terminated cmd1 |
  nul_terminated cmd2 | xargs -r0 ...

But that means running a few redundant tr commands. Alternatively, you can run:

find ... -print0 | nul_terminated eval 'cmd1 | cmd2' | xargs -r0 ...
  • 1
    Doesn't this defeat the primary reason¹ for using \x0 instead of \n to delimit the values? (¹ so you can cope with values that might contain \n) – Thedward Nov 12 '14 at 22:02
  • @Thedward, no, on the contrary -print0 | tr '\n\0' '\0\n' has lines representing the file paths where newline characters in them have been converted to \0. So, if you take the first line with head -n 1 and convert the \0s back to newlines with tr '\0\n' '\n\0' again, you've got the first file path NUL-delimited with its embedded newline characters. – Stéphane Chazelas Nov 12 '14 at 22:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.