I'm confusing when I use /bin/zsh -c with print command, there is no output.
How can I get foo in next prompt with /bin/zsh -c?
$ print -z foo
$ foo
(↑this is expected result that foo is in next prompt)
$ /bin/zsh -c print -z foo
$
(not show foo in next prompt)
This happens because -z option prints into editing buffer (for the next prompt). So if you run:
$ /bin/zsh -c 'print -z FOO'
it will push FOO in the next prompt editing buffer of that /bin/zsh -c sub-shell. But that sub-shell will exit immediately after running print -z FOO and so FOO will end up discarded in void. There's no way that subshell can manipulate its parent shell editing buffer (they are completely different processes with their own editing buffers).
If you just want to inject FOO to the next command line of current shell just run:
$ builtin print -z FOO
and the next prompt will look like this (_ being cursor waiting for user input):
$ FOO_
Run it in the current shell, instead of in a child.
$ print -z echo hello
$ echo hello <-- Already in the buffer, only pressing ENTER now
hello
$
Related
It seems zsh doesn't honor globs inside variable patterns, in ${var##$pat} parameter expansions:
$ zsh -c 'pat=/*; var=/etc/; echo "$var $pat"; echo "${var##$pat}"'
/etc/ /*
/etc/
# sh result: empty
However, if $pat does not contain *, zsh and sh behave similarly:
$ zsh -c 'pat=/; var=/etc/; echo "$var $pat"; echo "${var##$pat}"'
/etc/ /
etc/
# sh result: same
zsh --emulate sh gives, of course, sh-compatible results. But if I want to stay in zsh emulation, is there any setopt option that changes this behavior? I've looked (briefly) in the docs and I can't really find the reason for this difference.
In zsh, variable contents will only be treated as a pattern if you
ask for that, with a ${~spec} expansion or the (very broad and therefore slightly dangerous) GLOB_SUBST option:
pat=/*t
var=/etc/
print "${var##$pat}"
#=> /etc/
print "${var##$~pat}"
#=> c/
setopt glob_subst
print "${var##$pat}"
#=> c/
This is described in the zshexpn man page, in the section for string substitution expansion ${name/pattern/repl}.
I'm new to zsh and am trying to bind a key sequence to a function with the following in my .zshrc:
say_hello(){
echo "hello"
}
zle -N say_hello
bindkey '^Y' say_hello
Pressing Ctrl-Y will call the function and I'll see "hello" printed to the terminal but after I need to press Enter again before I'm given another zsh prompt. Calling the function by just typing in say_hello at the zsh prompt and pressing Enter does what I want - I see hello printed and then I'm given another zsh prompt. How can I get this behavior when binding the function to a key sequence?
Above is a simple example, really the function I'm trying to write is below:
my_cd() {
if [[ "$#" -ne 0 ]]; then
cd $(autojump $#)
return
fi
dir_to_cd_to=$(fasd_cd -dl | fzf --height 40% --reverse --inline-info)
# above isn't so important - dir_to_cd_to could be obtained in any way
cd "$dir_to_cd_to"
}
zle -N my_cd
bindkey -v '^Y' 'my_cd'
To display messages in a zle widget, you're supposed to use zle -M rather than echo. echo will output your message at whatever the current cursor position is which isn't especially helpful. If you really want to use echo, calling zle reset-prompt afterwards will redraw a fresh prompt. If you don't want a potential mess in your terminal, consider starting with \r to move the cursor to the beginning of the line and ending with $termcap[ce] to clear to the end of the line.
I am trying to source a third party script in zsh (named setup_env.sh stored in ~/), that has following lines in the beginning to guard against accidental execution:
#!/bin/sh
# Guard the script against execution - it must be sourced!
echo $0 | egrep 'setup_env.sh' > /dev/null
if [ $? -eq 0 ]; then
echo ""
echo "ERROR: the setup file must be SOURCED, NOT EXECUTED in a shell."
echo "Try (for bash) : source setup_env.sh"
echo "Or (eg. for ksh): . setup_env.sh"
exit 1
fi
# export some environment variables
...
When I source this script with source ~/setup_env.sh, I see the error message shown in the above code block.
From the script it's apparently visible that it's not written with zsh in mind. But I still want to know why zsh behaves this way, and if it's possible to source the script as it is.
I could source the script as it is without error using bash.
I could also source it in zsh after commenting out the guard block in the beginning of the script.
Can someone explain this difference in behavior for source command between zsh and bash?
zsh/bash have different ways to detect sourcing, following should work for both :
if [[ -n $ZSH_VERSION && $ZSH_EVAL_CONTEXT == toplevel ]] || \
[[ -n $BASH_VERSION && $BASH_SOURCE == $0 ]]; then
echo "Not sourced"
exit 1
fi
To explain a little more, when you run :
source setup_env.sh
# setup_env.sh containing "echo $0"
In zsh, $0 == setup_env.sh
In bash, $0 == bash
Sometimes I use multiline commands in zsh:
❯ echo \
> a \
> multiline \
> command
When editing the command after pulling it from a history search, I can change the content of individual lines. However, I can't figure out how to insert another line:
# I want to insert another line after "multiline"...
❯ echo \
> a \
> multiline \ # but hitting <return> here just runs the command, even though there's a backslash at the end of the line
> command
How can I insert a newline in the middle of a multiline command pulled from history?
You can use ESC-Return.
FWIW, I tested it on Debian Jessie, zsh 5.0.7 and it works there.
You can use self-insert-unmeta to bind Alt+Return to insert a literal newline without accepting the command:
bindkey '^[^M' self-insert-unmeta
To use your example: Hitting Alt+Return at the cursor position (#)
% echo \
a \
multiline \#
command
will get you this:
% echo \
a \
multiline \
#
command
This works not only when editing history, but also when typing commands. So you can prepare several commands in a script like fashion and accept them with a single Return.
For example pressing Alt+Return instead of # in this example:
% echo command 1#
echo command 2#
echo command 3
will do the same thing as the command echo command 1; echo command 2; echo command 3 and produce this output:
command 1
command 2
command 3
(A summary of answers from https://unix.stackexchange.com/questions/6620/how-to-edit-command-line-in-full-screen-editor-in-zsh)
zsh comes with a function that can be used to open the current command line in your favorite editor. Add the following lines to your .zshrc:
autoload -z edit-command-line
zle -N edit-command-line
bindkey "^X^E" edit-command-line
The first line loads the function. The second line creates a new widget for the Z shell line editor (zle) from the function of the same name. The third line binds the widget to Control-X Control-E. If you use the vi bindings rather than emacs key bindings, use something like
bindkey -M vicmd v edit-command-line
instead (which binds the widget to v in vicmd mode).
If using bindkey -v mode, you can also use the default o/O commands from vicmd mode to add a newline line and enter insert mode in it, respectively above or below the current line.
Just to note, if you want to comment in a multiline command, you could use:
❯ echo `#first comment` \
a `#second comment` \
multiline \
command
CTRL + Enter (Return) for Windows/WSL
CTRL +X CTRL+E for Mac
Edited as per the comment below
Sounds like an appropriate place to use a shell script file instead no?
#!/bin/zsh
my
commands
here
I can even add a new line at a later time.
When there is no files inside the folder the below script goes inside the for loop. Not sure what i can modify so that it doesn't go inside the for loop. Also when there is no files inside the directory exit status should be success. Wrapper script checks the exit status of the below script
FILESRAW ="/exp/test1/folder" .
for fspec in "$FILESRAW"/* ; do
echo "$fspec"
if [[ -f ${fspec} ]] ; then
..... processing logic
else
... processing logic
fi
done
if using bash,
you can set nullglob
shopt-s nullglob
if you have hidden files,
shopt -s dotglob
with ksh,
#!/bin/ksh
set -o noglob
for file in /path/*
do
....
done
for fspec in `dir $FILESRAW` ; do
To exit if $FILESRAW is empty:
[ $( ls "$FILESRAW" | wc -l ) -eq 0 ] && exit 0
If this test precedes the loop, it will prevent execution from reaching the for loop if $FILESRAW is empty.
When $FILESRAW is empty, "$FILESRAW"/* expands to "/exp/test1/folder/*", as ghostdog74 points out, you can change this behavior by setting nullglob with
shopt -s nullglob
If you want hidden files, set dotglob as well:
shopt -s dotglob
Alternately, you could use ls instead of globing. This has the advantage of working with very full directories (using a pipe, you won't reach the maximum argument limit):
ls "$FILESRAW" | while read file; do
echo "$file"
This becomes messier if you want hidden files, since you'll need to exclude . and .. to emulate globing behavior:
ls -a "$FILESRAW" | egrep -v '^(\.|\.\.)$' | while read file; do
echo "$file"
if you are using ksh,
try putting this in front of for loop so that it won't go inside it.
"set -noglob"
Even I have got the same problem, but I was able to resolve it by doing this.