adding a function to .zshrc changes the command - zsh

I added the following function to my .zshrc
function jptt(){
# Forwards port $1 into port $2 and listens to it
ssh -N -f -L localhost:$2:localhost:$1 remoteuser#remotehost
}
then I am running jptt 1 2
and get the following error:
Bad local forwarding specification localhost:2ocalhost:1
It is strange that I lose :l after the 2
the function is working when as I tried to replace the command with a simple line and it worked. I also run the ssh command separately and it works well.

The expression $x:l applies the lower-casing modifier to your x variable. The following example illustrates this:
pax> x=ABC
pax> echo $x:lnnn
abcnnn
pax> echo ${x}:lnnn
ABC:lnnn
The first section gives you the lower-case variant, and therefore the modifier is not considered part of your output string. The second section shows how you can prevent this variable expansion by using braces to ensure the :l is not treated as a modifier. In your specific case, that would be done with the line:
ssh -N -f -L localhost:${2}:localhost:${1} remoteuser#remotehost
It's actually a good idea to get into the habit of bracing parameter names as much as possible since there are other cases where this might adversely affect you.

Use ${1} and ${2} . Zsh supports : csh string modificators and :l has special meaning (to lowercase variable in front of it) and that's why it is consumed from $1:localhost.

Related

Avoid variable expansion in zsh

If I use the zsh shell and execute the following command I get
$zsh
$echo '$_GET["test"]'
preexec: bad math expression: operand expected at `"test"'
$echo '$_GET[]'
preexec: invalid subscript
In bash I get what I expect:
$bash
$echo '$_GET["test"]'
$_GET["test"]
I assume that zsh is trying to expand the $_GET variable. How can I avoid this? I always expected this to only happen within double quotes anyhow.
[update]
I found the following three lines in the .zshrc:
# Display last command interminal
echo -en "\e]2;Parrot Terminal\a"
preexec () { print -Pn "\e]0;$1 - Parrot Terminal\a" }
After commenting them out everything seems to work as expected.
What I understand is that preexec is executed after a command in the terminal has been submitted but before it is executed. The $1 is the command that one submitted.
I still do not understand the purpose of the two lines but is it because of the double quotes in the preexec print statement that the variables are expanded?
The combination of print -P together with the expansion of $1 is killing you. With this, you first get a "normal" expansion of $1, yielding something like "\e]0;echo '$_GET["test"]'...". Now -P causes print to do a prompt expansion on this string, which means that it has to expand $_GET["test"] as well. This causes the error.
I suggest to remove the -P, in particular since you don't have any characters in your string which would benefit from prompt expansion.

how to echo literal variable value with zsh

I have a simple shell function to convert a *nix style path to Windows style (I happen to be using Windows Subsystem for Linux).
# convert "/mnt/c/Users/josh" to "C:\Users\josh"
function winpath(){
enteredPath=$1
newPath="${enteredPath/\/mnt\/c/C:}" # replace /mount/c/ with C:
newPath="${newPath//\//\\}" # replace / with \
echo $newPath
}
The desired behavior is:
$ winpath /mnt/c/Users/josh
C:\Users\josh
This works correctly in bash, but in zsh, echo seems to do some extra interpolation of the $newPath value. It behaves like this:
$ winpath /mnt/c/Users/josh
C:sers\josh
What character sequence is echo interpolating and why is it remove the \U? Most importantly, how do I return the literal value?
I've tried digging through the zsh documentation, but it's a jungle. Thanks in advance!
zsh processes certain escape sequences that bash does not by default. \U introduces 4-byte Unicode codepoint, but since the following 8 characters are not a valid hexadecimal number, no character is substituted.
I would recommend using printf, as its behavior is much more predictable from shell to shell.
printf '%s\n' "$newPath"
The problem is that you are using the internal command echo, instead of the external one. If you would write
command echo $newPath
you would get the expected output. command forces zsh to look up the command word according to the current PATH, ignoring internal commands, aliases or functions of the same name.

Renaming multiple files using parameter unix

I have a rename script below rename.sh. I want to introduce a variable such that I can pass a date argument when executing the script like./rename.sh 20151103 such that 20151103 replaces 20140306 in the script.
for f in *.CDR*; do
echo mv "$f" "${f/-20140306/-0-20140306}"
done
Thinking of automating this as I don't want to manually edit the script each time i'm doing a rename. Any other method will be highly welcomed.
#!/bin/bash
pattern="$1"
for f in *.CDR*; do
echo mv "$f" "${f/-${pattern}/-0-${pattern}}"
done
Explanation:
The #!-line says we're running this as a bash script.
The script will populate the variables $1, $2 etc. with the arguments handed to it on the command line. These are called the positional parameters ($0 usually holds the name of the script).
We take $1, because we know that should contain the pattern we're replacing, and assign it to the variable $pattern. In much more complex scripts, here is where we would handle command line switches (with getopts, but that's an answer for another day).
We quote $1, just because. (It's good practice to quote user input, just to be sure no shell-globbing characters, such as * gets expanded).
The rest is the script like you had from before, but with the string 20140306 replaced by ${pattern}. I'm using ${pattern} rather than $pattern here for readability only. In general, you need to use ${a} rather than $a if you, for example, interpolate a string like "${a}nospaceafter".
Then it should just be a matter of making the script executable before testing it:
$ chmod +x rename.sh
This is the one of the method you can consider:
#!/bin/bash
input=$1
for f in *.CDR*; do
echo mv "$f" "${f/-$input/-0-$input}"
done

Make zsh complete arguments from a file

zsh is great but its completion system is very diverse. And the documentation lacks good examples. Is there a template for completing for a specific application. The completion would get its match data from a file, separated by newlines?
I tried modifying an older example of mine that takes match data "live":
~ % cat .zsh/completers/_jazzup
#compdef jazz_up
_arguments "2: :(`mpc lsplaylists|sed -e 's# #\\\\ #g'`)"
I could supply cat my_file there instead of mpc invocation and so on but would there be a more elegant way to do this simple task? And that completion there is placement-specific: can you provide an example where zsh would attempt to complete at any point after the program name is recognized?
The match data will have whitespaces and so on, the completion should escape the WS. Example of that:
Foo bar
Barbaric
Get it (42)
Now if that completion would be configured for a command Say, we should get this kind of behaviour out of zsh:
$ Say Fo<TAB>
$ Say Foo\ bar
$ Say Ge<TAB>
$ Say Get\ it\ \(42\)
Simple completion needs are better addressed with _describe, it pairs an array holding completion options and a description for them (you can use multiple array/description pairs, check the manual).
(_arguments is great but too complex.)
[...]
First create a file
echo "foo\nbar\nbaz\nwith spac e s\noh:noes\noh\:yes" >! ~/simple-complete
Then create a file _simple somewhere in your $fpath:
#compdef simple
# you may wish to modify the expansion options here
# PS: 'f' is the flag making one entry per line
cmds=( ${(uf)"$(< ~/simple-complete)"} )
# main advantage here is that it is easy to understand, see alternative below
_describe 'a description of the completion options' cmds
# this is the equivalent _arguments command... too complex for what it does
## _arguments '*:foo:(${cmds})'
then
function simple() { echo $* }
autoload _simple # do not forget BEFORE the next cmd!
compdef _simple simple # binds the completion function to a command
simple [TAB]
it works. Just make sure the completion file _simple is placed somewhere in your fpath.
Notice that : in the option list is supposed to be used for separating an option from their (individual) description (oh:noes). So that won't work with _describe unless you quote it (oh\:yes). The commented out _arguments example will not use the : as a separator.
Without changing anything further in .zshrc (I already have autoload -Uz compinit
compinit) I added the following as /usr/local/share/zsh/site-functions/_drush
#compdef drush
_arguments "1: :($(/usr/local/bin/aliases-drush.php))"
Where /usr/local/bin/aliases-drush.php just prints a list of strings, each string being a potential first argument for the command drush. You could use ($(< filename)) to complete from filename.
I based this on https://unix.stackexchange.com/a/458850/9452 -- it's surprising how simple this is at the end of the day.

cron syntax for date

The following statement work at command prompt. But does not work in a cron.
myvar=`date +'%d%m'`; echo $myvar >> append.txt
The cron log shows that only a part of the date statement is run.
How do I use it in a cron?
Escape the percent signs with a backslash (\%).
My general rule of thumb is "do not write scripts in the crontab file". That means I don't place anything other than a simple script name (with absolute path) and possibly some control arguments in the crontab file. In particular, I do not place I/O redirection or variable evaluations in the crontab file; such things go in a (shell) script run by the cron job.
This avoids the trouble - and works across a wide variety of variants of cron, both ancient and modern.
from man 5 crontab:
The sixth field (the rest of the
line) specifies the command to be run.
The entire command portion of the line, up to a newline or % character, will be
executed by /bin/sh or by
the shell specified in the SHELL variable of the cronfile.
Percent-signs (%) in the command, unless escaped with backslash (), will be changed into
newline characters, and all
data after the first % will be sent to the command as standard input.
Your %s are being changed to newlines, and the latter part of your command is being fed to the command as stdin. As Ignacio says, you need to escape the %s with a \

Resources