2

I know how to split a string on a printable separator character; e.g. if the separator is ,:

FOO='x,y,z'
printf "%s\n" ${(s:,:)FOO}
x
y
z

But what if the separator is a control character, such as \034? For example

FOO=$'x\034y\034z'

I've tried putting everything I can think of between the :'s in the (s:...:) qualifier, including \034, $'\034', and several others, but nothing I've tried splits the original string.

(One solution that is not acceptable would be to perform a global substitution that replaces all occurrences of the original delimiter with, for example, ,, and then to split the resulting string with (s:,:). The reason for ruling out such a solution is that the rational for using `$'\034' as delimiter in the first place is to reduce the chance that the delimited text would contain a delimiter. Replacing the original delimiter with a printable one completely defeats this rationale.)


EDIT: regarding putting the delimiter in a variable, I tried the following test script

#!/usr/bin/env zsh

DELIM=$'\034'
FOO="x${DELIM}y${DELIM}z"

BAR=(${(ps:$DELIM:)FOO})
printf "%s\n" $BAR

BAZ=(${(ps:\034:)FOO})
printf "%s\n" $BAZ

The output I get is

x^\y^\z
x
y
z

(where I've used ^\ to simulate the appearance of the printed \034 on my terminal.)

FWIW, my shell version is zsh 5.0.7 (x86_64-pc-linux-gnu).

4
  • 1
    Work well for me with zsh 5.2
    – cuonglm
    Commented Mar 2, 2016 at 4:54
  • @cuonglm: oh well, I guess it's time for me to upgrade, because that's the sort of thing that should work. :)
    – kjo
    Commented Mar 2, 2016 at 4:57
  • 1
    You need 5.0.8 or later for using variable as delimiter. Read zsh.sourceforge.net/releases.html
    – cuonglm
    Commented Mar 2, 2016 at 5:03
  • @cuonglm: thanks for the info; it's too bad this sort of information is not in the documentation (in the case of my shell's version that would mean an explicit mention that separators in variables are not supported)... Then again, the business with the p flag is in the documentation, and I still managed to miss it. :/
    – kjo
    Commented Mar 2, 2016 at 5:11

1 Answer 1

4

You need to add p flag to make the following flag recognize escape sequences:

$ FOO=$'x\034y\034z'
$ print -rl -- ${(ps:\034:)FOO}
x
y
z

If you don't want to hard-code the delimiter (require version >= 5.0.8):

$ DELIM=$'\034'
print -rl -- ${(ps:$DELIM:)FOO}
5
  • Thanks! After I posted my question it occurred to me that I could stick the separator inside a variable, say DELIM, and do something with eval ... \${(s:$DELIM:)FOO}. This is definitely much uglier than your solution, but being able to avoid hard-coding the delimiter everywhere has its merits.
    – kjo
    Commented Mar 2, 2016 at 3:34
  • 1
    @kjo: No, you don't have to hard-code. Try DELIM=$'\034'; print -rl -- ${(ps:$DELIM:)FOO}
    – cuonglm
    Commented Mar 2, 2016 at 3:42
  • Hmmm... I tried something like that, in fact, when I first saw your answer, but for some reason it did not work. More specifically, I had tried SPLIT=(${(s:$DELIM:)FOO}), but $SPLIT ended up with only one element (equal to the original $FOO). In contrast, with the version where the delimiter is hard-coded, namely, SPLIT=(${(s:\034:)FOO}), $SPLIT is an array of several elements, as desired...
    – kjo
    Commented Mar 2, 2016 at 4:20
  • @kjo: You missed the p flag, didn't you?
    – cuonglm
    Commented Mar 2, 2016 at 4:23
  • Sorry, those were typos when I wrote the comment; I added an EDIT to my post where I show exactly the code that I tried, along with the generated output. Again, sorry for the confusion.
    – kjo
    Commented Mar 2, 2016 at 4:42

You must log in to answer this question.

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