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 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. :) 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 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. :/ 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. 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} 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... Commented Mar 2, 2016 at 4:20
  • @kjo: You missed the p flag, didn't you? 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. Commented Mar 2, 2016 at 4:42

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.