4

I am trying to print a sorted list of all zsh options, with set options colored green and unset options colored red. I cannot get sort to work properly with on the colored lines though.The below prints all the red options followed by all the green options:

print -lP "%F{green}"${^$(setopt)} "%F{red}"${^$(unsetopt)} | sort

I figured this was because print -P is expanding the format strings such that each option line starts with ^[[31m for red and ^[[32m for green. Looking at the sort manpage, I saw two options that might help:

-i, --ignore-nonprinting consider only printable characters

-k, --key=POS1[,POS2] start a key at POS1 (origin 1), end it at POS2 (default end of line)

So I tried:

print -lP "%F{green}"${^$(setopt)} "%F{red}"${^$(unsetopt)} | sort -i

and

print -lP "%F{green}"${^$(setopt)} "%F{red}"${^$(unsetopt)} | sort --key=<N>

Where I tried setting <N> to many different numbers. In all cases, I got the same results (all red options before all green). How can I solve this?

1
  • The only unprintable character is ^[, not the whole escape sequence. Oct 26, 2021 at 7:49

2 Answers 2

3

-k option of sort takes two numerical arguments: field and character. You want to sort on 6th character of first field. It is 6th character because %F{green} is replaced by ESC[32m. So this should work:

print -lP "%F{green}"${^$(setopt)} "%F{red}"${^$(unsetopt)} | sort -k 1.6
3

In an escape sequence like ^[[31m, the escape character ^[ is non-printing, but the other characters [31m are printing characters. So sort -i won't help you: it ignores the escape characters but still sorts [31mred[0m before [32mgreen[0m.

A generic way to sort data according to criteria that go beyond the built-in abilities of the sort utility is to double up the data on each line, massage it so that the first copy is the sort key, sort the lines, and finally strip off the first part. For example, assuming that the data you want to sort doesn't contain tab characters:

awk '{s=$0; gsub(/\033\[[ -?]*[@-~]/,"",s); print s "\t" $0}' |
sort |
awk 'BEGIN {RS="\t"} {print $2}'

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .