46

In python:

s = '1::3'
a = s.split(':')
print(a[0]) # '1' good
print(a[1]) # '' good
print(a[2]) # '3' good

How can I achieve the same effect with zsh?

The following attempt fails:

string="1::3"
a=(${(s/:/)string})
echo $a[1] # 1
echo $a[2] # 3 ?? I want an empty string, as in Python
0

2 Answers 2

75

The solution is to use the @ modifier, as indicated in the zsh docs:

string="1::3"
a=("${(@s/:/)string}") # @ modifier

By the way, if one has the choice of the delimiter, it's much easier and less error prone to use a newline as a delimiter. The right way to split the lines with zsh is then:

a=("${(f)string}")

I don't know whether or not the quotes are necessary here as well...

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks. Are you able explain how it works though, so many brackets is making it confusing!
If you're trying to split on slashes, the original /s can be replaced with |s, it seems (e.g. a=(${(s|/|)string}")). I can't seem to find any documentation on this, though, so maybe there's some subtle behaviour change.
@Harry, the linked documentation says this: "Any character, or the matching pairs ‘(...)’, ‘{...}’, ‘[...]’, or ‘<...>’, may be used in place of a colon as delimiters, …." In Olivier's code, the character "used in place of a colon" was slash; in yours, it's vertical bar. So, no behavior change.
This worked correctly in the command line, but when I moved it into a function, I got the "unrecognized modifier" error, any idea?
So much easier to grok than the IFS bash equivalent. Thanks
|
10

This will work in both zsh (with setopt shwordsplit or zsh -y) and Bash (zero-based arrays):

s="1::3"
saveIFS="$IFS"
IFS=':'
a=(${s})
IFS="$saveIFS"

13 Comments

This worked for me on the command line but would not work for me in a function even though I made sure shwordsplit was set. I'm not sure why it didn't.
@ws_e_c421: It works for me in a function in zsh and Bash. You didn't say which you are using. You should be able to echo ${a[1]} and echo ${a[3]} and get 1 and 3, respectively (the subscripts should be 0 and 2 for Bash). If it's short, post here what you tried and how exactly it failed. If it's a little longer, post a new question and link to it here.
IMHO you should scope environment variable changes like that, e.g. IFS=':' cmd args..., so the change is coupled with the context/lifespan where it's expected/used. Then it's Not Your Job to know the prior values you aren't using, or to manage or recreate the outside environment, etc. - instead they clean themselves up.
@JohnP: That kind of scoping doesn't work with a variable assignment (in this case creating an array from a scalar). And another kind of scoping, using a subshell, doesn't work here because the array being created would also be scoped to the subshell and thus unavailable later. But your point is valid in contexts where it's useful.
@JohnP: It does for me. This is the output I see from the echo inside your function (with the missing semicolon added): 1, 1::3, ':'. The 1 is the first element of the array a which is the result of splitting s. See my earlier comments regarding better ways to output arrays. Something I didn't mention earlier is using local IFS=':' inside a function to limit the scope (but that doesn't apply to your original point).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.