ZSH subString extraction

You can use the cut command:

echo $1 | cut -c1 
echo $1 | cut -c2-

Use $() to assign these values to variables:

ARG_FIRST=$(echo $1 | cut -c1)
ARG_REST=$(echo $1 | cut -c2-)
echo ARG_FIRST=$ARG_FIRST
echo ARG_REST=$ARG_REST

You can also replace $() with backticks, but the former is recommended and the latter is somewhat deprecated due to nesting issues.


A1

As others have said, you need to wrap it in curly braces. Also, use a command interpreter (#!...), mark the file as executable, and call it directly.

#!/bin/zsh

echo $1
echo ${1[1,1]}

A2

The easiest way to extract a substring from a parameter (zsh calls variables parameters) is to use parameter expansion. Using the square brackets tells zsh to treat the scalar (i.e. string) parameter as an array. For a single character, this makes sense. For the rest of the string, you can use the simpler ${parameter:start:length} notation instead. If you omit the :length part (as we will here), then it will give you the rest of the scalar.

File test:

#!/bin/zsh

echo ${1[1]}
echo ${1:1}

Terminal:

$ ./test Hello
H
ello

A3

As others have said, you need (preferably double) square brackets to test. Also, to test if a string is NULL use -z, and to test if it is not NULL use -n. You can just put a string in double brackets ([[ ... ]]), but it is preferable to make your intentions clear with -n.

if [[ -z "${ARG_FIRST}" ]]; then
  ...
fi

Also remove the space between #! and /bin/zsh. And if you are checking for equality, use ==; if you are assigning a value, use =.

RE:EDIT2:

  • Declare all parameters to set the scope. If you do not, you may clobber or use a parameter inherited from the shell, which may cause unexpected behavior. Google's shell style guide is a good resource for stuff like this.
  • Use builtins over external commands.
  • Avoid backticks. Use $(...) instead.
  • Use single quotes when quoting a literal string. This prevents pattern matching.
  • Make use of elif or case to avoid nested ifs. case will be easier to read in your example here, but elif will probably be better for your actual code.

Using case:

#!/bin/zsh

typeset ARG_FIRST="${1[1]}"
typeset ARG_REST="${1:1}"

echo $1
echo 'ARG_FIRST='"${ARG_FIRST}"
echo 'ARG_REST='"${ARG_REST}"

case "${ARG_FIRST}" in
  ('') echo 'nullArgs' ;;
  ('@') echo '@Args' ;;
  (*)
    # Recommended formatting example with more than 1 sloc
    echo 'regularArgs'
    ;;
esac

using elif:

#!/bin/zsh

typeset ARG_FIRST="${1[1]}"
typeset ARG_REST="${1:1}"

echo $1
echo 'ARG_FIRST='"${ARG_FIRST}"
echo 'ARG_REST='"${ARG_REST}"

if [[ -z "${ARG_FIRST}" ]]; then
  echo nullArgs
elif [[ '@' == "${ARG_FIRST}" ]]; then
  echo @Args
else
  echo regularArgs
fi

RE:EDIT3

  • Use "$@" unless you really know what you are doing. Explanation.

So, I wrote a shell script in a file named test

$ sh test hello

This isn't a zsh script: you're calling it with sh, which is (almost certainly) bash. If you've got the shebang (#!/bin/zsh), you can make it executable (chmod +x <script>) and run it: ./script. Alternatively, you can run it with zsh <script>.

the result fails. What's wrong with the code?

You can wrap in braces:

echo ${1}        # This'll work with or without the braces.
echo ${1[3,5]}   # This works in the braces. 
echo $1[3,5]     # This doesn't work. 

Running this: ./test-script hello gives:

./test-script.zsh hello 
hello
llo
./test-script.zsh:5: no matches found: hello[3,5]

Also I don't know how to extract subString from n to the last. Perhaps do I have to use Array split by regex?

Use the [n,last] notation, but wrap in braces. We can determine how long our variable is with, then use the length:

# Store the length of $1 in LENGTH. 
LENGTH=${#1}
echo ${1[2,${LENGTH}]}  # Display from `2` to `LENGTH`. 

This'll produce ello (prints from the 2nd to the last character of hello).

Script to play with:

#!/usr/local/bin/zsh

echo ${1}      # Print the input
echo ${1[3,5]} # Print from 3rd->5th characters of input
LENGTH=${#1}   
echo ${1[2,${LENGTH}]} # Print from 2nd -> last characters of input.

You can use the cut command:

But that would be using extra baggage - zsh is quite capable of doing all this on it's own without spawning multiple sub-shells for simplistic operations.

Tags:

Zsh