Capturing and testing output command in ZSH - zsh

I have tried countless ways to get what I want, but nothing seems to work. I always end up with something like 2:not found.
I want to capture the output of a command, and then test if it equals "!", like so:
function test() {
local testv=$(command) 2>/dev/null
if [ $(#testv) == "!" ]; then
echo "Exclamation mark!"
else
echo "No exclamation mark."
fi
}
How should I rewrite the code above to avoid the error test:2: = not found?

This should work:
if [ $testv = '!' ]; then
There were several problems here:
$(...) runs a command and substitutes its output; you want to substitute a variable value, so use $var or ${var}.
I have no idea what the # was doing there. ${#var} will get the length of $var, but that's not what you want here.
The test command (which [ is a synonym for) doesn't understand ==, so use = (if you're a C programmer that'll look wrong, but this is shell not C).
I don't think this is a problem in a script, but for interactive input "!" doesn't do what you expect. I used '!' to make sure the exclamation mark couldn't be interpreted as a history option.
Alternately, you could use [[ ]] instead of [ ], since it understands == (and has somewhat cleaner syntax in general):
if [[ $testv == '!' ]]; then
BTW, I'm trusting from the tag that this script is running in zsh; if not, the syntax will be a bit different (basic shells don't have [[ ]], and anything other than zsh will do unwanted parsing on the value of $testv unless it's in double-quotes). If you're not sure (or want it to be portable), here's a version that should work in any posix-compliant shell:
if [ "$testv" = '!' ]; then

Try with this:
local testv=$(command 2>/dev/null)
since it's the output of the command you want to redirect.
(I have no idea what you mean by $(#testv) though.)

Related

DRYer prompt and assignment in Zsh

I would like to do something that accomplishes the same thing that this does:
[[ -z "$TICKET_NUMBER" ]] && read "TICKET_NUMBER?Ticket Number? "
but that is more condensed, along these lines (but which actually works):
: ${TICKET_NUMBER:=$(read "TICKET_NUMBER?Ticket: ")}
I've looked in the Zsh docs for read to see if there's a way to pass the input to read to STDOUT, but nothing looks like it can do that.
The ideal would be some command that passes the value directly with as little ceremony and repetition as possible. Imagine a get_value command:
: ${TICKET_NUMBER:=$(get_value "Ticket: ")}
The last argument of the previous command is stored in the _ parameter, so you can capture the argument to the -v operator.
test -v TICKET_NUMBER || read "$_?Ticket? "
This is a little ugly, but it works:
: ${TICKET_NUMBER:=$(read "?Ticket: "; echo "$REPLY")}
It's certainly not the ideal I'm going for, but it might be one step closer.

Zsh returning `<function>:<linenumber> = not found`

I used the have the following tmux shortcut function defined in a separate script and aliased, which worked fine but was messy. I decided to move it to my .zshrc where it naturally belongs, and encountered a problem I wasn't able to figure out.
function t () {re='^[0-9]+$'
if [ "$1" == "kill" ]
then
tmux kill-session -t $2
elif [[ "$1" =~ "$re" ]]
then
tmux attach-session -d -t $1
fi}
I source my .zshrc, call the function, and get:
t:1: = not found
I know the function is defined:
╭─bennett#Io [~] using
╰─○ which t
t () {
re='^[0-9]+$'
if [ "$1" == "kill" ]
then
tmux kill-session -t $2
elif [[ "$1" =~ "$re" ]]
then
tmux attach-session -d -t $1
fi
}
I'm assuming this is complaining about the first line of the function. I've tried shifting the first line of the function down several lines, which doesn't change anything except which line the error message refers to. Any clue what's going on? I haven't found anything relating to this specific issue on SO.
The command [ (or test) only supports a single = to check for equality of two strings. Using == will result in a "= not found" error message. (See man 1 test)
zsh has the [ builtin mainly for compatibility reasons. It tries to implement POSIX where possible, with all the quirks this may bring (See the Zsh Manual).
Unless you need a script to be POSIX compliant (e.g. for compatibility with other shells), I would strongly suggest to use conditional expressions, that is [[ ... ]], instead of [ ... ]. It has more features, does not require quotes or other workarounds for possibly empty values and even allows to use arithmetic expressions.
Wrapping the first conditional in a second set of square-brackets seemed to resolve the issue.
More information on single vs double brackets here:
Is [[ ]] preferable over [ ] in bash scripts?

shell glob pattern trouble

I'm trying to print out an out the corresponds to an input. The input would be a number and the output would print out the number in english. I am having trouble with the syntax/regular expressions in unix. If I were to run this with an input of 277, there would only be "one-hundred" as output. Where it should be "one-hundred twenty." Is there something wrong with it..? I've looked everywhere and it seems right.
I know it's not complete yet but it is bothering me. Any tips?
case "$1" in
1[0-9][0-9]) # 100's
echo one-hundred
if [ "$1" == 12[0-9] ]
then
echo twenty
fi
;;
The regex syntax is =~ not == so your if needs to be [[ "$1" =~ 12[0-9] ]] though not every shell supports that syntax

Changing the global “path” from within a function?

My zshenv file has a bunch of lines like
if [[ -d "$HOME/bin" ]]; then
path=($HOME/bin $path)
fi
I thought I’d try to factor this pattern out into a function. I replaced it with
function prepend_to_path_if_exists() {
if [[ -d $1 ]]; then
path=($1 $path)
fi
}
prepend_to_path_if_exists("$HOME/bin")
but this gives the error
/Users/bdesham/.zshenv:8: missing end of string
where line 8 is the one where I’m calling prepend_to_path_if_exists. What exactly is causing this error, and how can I make this function work? I’m using zsh 5.0.5 on OS X 10.10.1.
You could call functions as with usual command executions like this (without ()):
prepend_to_path_if_exists "$HOME/bin"
It seems that zsh try to expand the glob prepend_to_path_if_exists(…) rather than to call the function.
TL;DR: Prepending emelemnts to $path would be accomplished by a little cryptic way:
(I'm not quite sure that the below form is preferable for anyone though.)
# `typeset -U` uniqify the elements of array.
# It could be good for $path.
typeset -U path
# prepending some paths unconditionally,
path[1,0]=(\
$HOME/bin \
$HOME/sbin \
)
# then filtering out unnecessary entries afterward.
path=(${^path}(-/N))
The $path[x,0]=… is prepending(splicing) element(s) to array taken from the below:
So that's the same as VAR[1,0]=(...) ? It doesn't really "look" very
much like prepend to me.
-- Greg Klanderman (http://www.zsh.org/mla/workers/2013/msg00031.html)
The ${^path}(-/N) expands the glob qualifires -/N on the each $path elements.
(Without ^ in the parameter expansion, the last elements of array will be evaluated, so it is mandatory in this case.)
The glob qualifires -/N means that "symbolic links and the files they point to"(-) the "directory"(/). And when it does not match anything do not raise errors (N).
In short, it would keep exsisting directories only for $path.

How to create options in KSH script

I am creating a KSH interface script that will call other scripts based on the users input. The other scripts are Encrypt and Decrypt. Each one of these scripts receive parameters. I have seen someone execute a script before using "-" + first letter of a script name before. How do I do this for my script? So for example if my script is called menu and the user typed in : menu -e *UserID Filename.txt* the script would run and the encrypt script would be executed along with the associated parameters. So far my script takes in the encrypt/decrypt script option as a parameter. Here is my script:
#!/bin/ksh
#I want this parameter to become an
action=$1
if [ $1 = "" ]
then
print_message "Parameters not satisfied"
exit 1
fi
#check for action commands
if [ $1 = "encrypt" ]
then
dest=$2
fileName=$3
./Escript $dest $fileName
elif [ $1 = "decrypt" ]
then
outputF=$2
encryptedF=$3
./Dscript $outputF $encryptedF
else
print "Parameters not satisfied. Please enter encrypt or decrypt plus-n arguments"
fi
Thanks for the help!
There isn't any kind of automatic way to turn a parameter into another script to run; what you're doing is pretty much how you would do it. Check the parameter, and based on the contents, run the two different scripts.
You can structure it somewhat more nicely using case, and you can pass the later parameters directly through to the other script using "$#", with a shift to strip off the first parameter. Something like:
[ $# -ge 1 ] || (echo "Not enough parameters"; exit 1)
command=$1
shift
case $command in
-e|--encrypt) ./escript "$#" ;;
-d|--decrypt) ./dscript "$#" ;;
*) echo "Unknown option $command"; exit 1 ;;
esac
This also demonstrates how you can implement both short and long options, by providing two different strings to match against in a single case statement (-e and --encrypt), in case that's what you were asking about. You can also use globs, like -e*) to allow any option starting with -e such as -e, -encrypt, -elephant, though this may not be what you're looking for.

Resources