2

I'm developing some file naming guidelines, and I'm trying to find a way to allow a draft document to list before a final revision.

Example: Currently my naming convention looks like this:

contract_foo_v1
contract_foo_v1~draft1
contract_foo_v1~draft2
contract_foo_v2
contract_foo_v2~draft1

The problem is that v1~draft should list before v1 (the final revision). Is there a character I can use that would list as follows:

contract_foo_v1?draft1
contract_foo_v1?draft2
contract_foo_v1
contract_foo_v2?draft1
contract_foo_v2

I also understand that there may be locale and program differences, so I'm after recommendations for a character that would work as requested in most cases (or as many cases as possible) - Thanks for any help

  • 2
    Why not use a revision control system such as Git, CVS or even RCS? – Kusalananda Aug 27 '19 at 6:56
  • Call the final version contract_foo_v1-final and the drafts contract_foo_v1-draftN? – Kusalananda Aug 27 '19 at 6:58
  • Thanks for the comments - I do use a vcs (git) for my stuff, but this is a naming guideline for the staff in the company who are not that technically inclined. I'm trying to avoid using the final keyword, to mitigate the risk of contract_somesupplier_v01-final-final-lastone-03.pdf – cleary Aug 27 '19 at 23:03
4

ls sorts the list of files based on their name which in your case doesn't contain newline characters. Even sort sorts on the contents of the lines which don't include the newline character, so here it's not about finding a character that sorts before newline, but one that sorts before nothing and you won't find one.

Now, GNU ls -v (for version-sort) does happen to sort that list of files in the order you want:

$ ls -v1
contract_foo_v1~draft1
contract_foo_v1~draft2
contract_foo_v1
contract_foo_v2~draft1
contract_foo_v2
contract_foo_v10

Without -v, since ls does a lexical sort, v10 would also sort before v2.

With GNU sort, you can also use sort -V on the output of ls (assuming your file names don't contain newline characters):

$ ls | sort -V
contract_foo_v1~draft1
contract_foo_v1~draft2
contract_foo_v1
contract_foo_v2~draft1
contract_foo_v2
contract_foo_v10

POSIXly, you could do:

$ ls | sort -t'~' -k1.15,1n -k2,2.1r -k2n
contract_foo_v1~draft1
contract_foo_v1~draft2
contract_foo_v1
contract_foo_v2~draft1
contract_foo_v2
contract_foo_v10

But that assumes the part before the version is always 14 characters long.

With the zsh, shell, you could define a glob sort order like:

drafts_first() {
  [[ $REPLY = *'~draft'* ]] || REPLY="$REPLY~release"
}
printf '%s\n' *(no+drafts_first)

You could also choose to name your files like contract_foo_v001_draft01 and contract_foo_v001_release (or contract_foo_v001_final as suggested by @Kusalananda; any word that sorts after draft), assuming you never have more than 999 versions per document and 99 drafts per version, then they would sort in the right order with ls alone or any tool that lists files in lexical order.

See also ls -rt to sort files by modification time (newest last), or the Om glob qualifier of zsh.

Now, if you add an extension (like .pdf) to those file names, then that becomes a whole different story:

$ LC_ALL=en_US.UTF-8 ls -1
contract_foo_v10.pdf
contract_foo_v1~draft1.pdf
contract_foo_v1~draft2.pdf
contract_foo_v1.pdf
contract_foo_v2~draft1.pdf
contract_foo_v2.pdf
$ LC_ALL=C ls -1
contract_foo_v1.pdf
contract_foo_v10.pdf
contract_foo_v1~draft1.pdf
contract_foo_v1~draft2.pdf
contract_foo_v2.pdf
contract_foo_v2~draft1.pdf

In my en_US.UTF-8 locale, both ~ and . are ignored in the first comparison pass, so v10 comes before v1~d because 0 sorts before d, and v1~draft comes before v1.pdf because draft sorts before pdf (that would be different with a .docx extension).

In the C locale, where there's only one pass and the sorting is based on byte value, ., 0 and ~ sort in that order. ~ is the printable character with highest value in ASCII, so it would be the poorest choice there. There are quite a few that come before . like #, -, %, all of them ignored in the first pass above in locales that follow iso14651_t1.

So if we fix the v2 vs v10 by using 0-padding and use - instead of ~, with .pdf extensions we get the order we want both in human locales and in C/C.UTF-8 locales where the order is based on code point:

$ LC_ALL=en_US.UTF-8 ls -1
contract_foo_v01-draft1.pdf
contract_foo_v01-draft2.pdf
contract_foo_v01.pdf
contract_foo_v02-draft1.pdf
contract_foo_v02.pdf
contract_foo_v10.pdf
$ LC_ALL=C ls -1
contract_foo_v01-draft1.pdf
contract_foo_v01-draft2.pdf
contract_foo_v01.pdf
contract_foo_v02-draft1.pdf
contract_foo_v02.pdf
contract_foo_v10.pdf
  • Not the answer that I was hoping for, but "no character sorts before nothing" is the reality I have to deal with. I was really hoping to avoid using the final keyword for the same reasons I mentioned in the comment above but I may have to (or at least use something alphabetical that makes sense internally) - thanks for the detailed reply on the ls usage, that is really handy to know (and I think I will be adjusting my aliases going forward!) – cleary Aug 27 '19 at 23:07
  • 1
    @cleary, see edit if those files have a .pdf extension as your comment suggests. Again, bear in mind that v10 sorts before v2 lexically. The usual way around that is to use 0-padding. – Stéphane Chazelas Aug 28 '19 at 6:55
  • that's very interesting ... I used PDF only as a generic file format for the purposes of the post. But, the vast majority of our internal documents are actually in opendocument format, which would also work well in this example (.odt, .ods, .odX). Thanks for the follow up, appreciate it! – cleary Aug 28 '19 at 23:33

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.