I'm writing a bash script that needs to both be able to cd in the current shell and use less to display longform text. To be able to cd, I understand that I need to source the script when I call it, which I've done via an alias in my ZSH config. However, when I do this, less breaks: instead of echo -e "$result" | less displaying its usual scrolling buffer, the long text gets dumped into the shell.
For context, this is a bash script acting as a wrapper for a Node.js script so as to be able to have native access to bash commands (like cd, open, etc.). The alias in my zshrc is as follows (with the path truncated): alias bk='source ~/.../bookmark/bookmark.sh'.
Is there any way to satisfy both the need to cd and the need to use less?
Fixed! This turned out to be an issue in my script's logic. I was using condition=$(echo $result | cut -c 1-3), but in reality need the first three characters (not columns) of $result, which I then did by using $result | head -c 3. What's interesting about this is that fetching the first three columns from $result when determined by running ./bookmark.sh works as an equivalent to fetching the first three characters, but running the alias yields the issue here.
Related
I'm confused about this code! Why # cant't play a role that takes the length of a string?
string="abcd"
!echo ${#string}
In fact, the code behind # has become commented and cannot be executed!
Any advice?
This works correctly, but you cannot mix python and bash variables in this way. Try this instead:
!string="abcd" && echo ${#string}
The two statements have to be on the same line because in IPython, each ! statement opens a temporary subshell and variables are not persisted between shells. If you want to use multiline bash programs, you can use the %%bash cell magic instead:
%%bash
string="abcd"
echo ${#string}
Here's my .zshrc:
alias inst="adb install -r -d"
notify() {
start=$(date +%s)
"$#"
notify-send "\"$(echo $#)\" took $(($(date +%s) - start)) seconds to finish"
}
When I run the command notify inst app.apk, I get an error:
notify:2: command not found: inst
0.00s user 0.00s system 0% cpu 0.001 total
Can anyone shed some light on why this doesn't work, and hopefully a way to make it work?
When the shell processes commands, among other things (e.g. PATH search) it will check to see if the first token/argument (whitespace delimited) belongs to an alias loaded in the current environment. If the alias you are trying to substitute is not the first token, the substitution will not happen. If your alias happens to not be the name of an executable on the PATH or current directory, the error will propagate back up to a command not found.
Since your question is about the Z Shell, zsh actually provides a lesser-known feature called global aliasing. If an alias is declared with the -g flag, zsh will make the appropriate substitution for not only the first token, but any token, regardless of order.
alias -g inst="adb install -r -d" should do the trick.
Keep in mind that this is a zsh only feature for portability reasons, and make sure that whatever script your writing has a shebang line that invokes the zsh shell: #!/usr/bin/env zsh
I would also recommend to not use zsh global aliasing in important or production scripts. For personal use, it is perfectly fine.
Don't use alias for scripting
First, according to the advanced bash scripting guide:
In a script, aliases have very limited usefulness.
So you may consider not using alias but a function for instance (still from the same page, 2 paragraph lower):
Almost invariably, whatever we would like an alias to do could be accomplished much more effectively with a function.
A hacky solution
If this is for a quick script for yourself using aliases you have in your .zshrc, there is still a way out.
alias foo='echo hello'
bar() {
`alias "$#" | cut -d\' -f2`
}
bar foo # => hello
Alias replacement
from alias man page:
The first word of each simple command, if unquoted, is checked to see if it has an alias. If so, that word is replaced by the text of the alias. The alias name and the replacement text can contain any valid shell input, including shell metacharacters, with the exception that the alias name can not contain `='.
The first word of the replacement text is tested for aliases, but a word that is identical to an alias being expanded is not expanded a second time. This means that one can alias ls to "ls -F", for instance, and Bash does not try to recursively expand the replacement text.
Aliases are not expanded when the shell is not interactive, unless the expand_aliases shell option is set using shopt .
The rules concerning the definition and use of aliases are somewhat confusing. Bash always reads at least one complete line of input before executing any of the commands on that line. Aliases are expanded when a command is read, not when it is executed. Therefore, an alias definition appearing on the same line as another command does not take effect until the next line of input is read. The commands following the alias definition on that line are not affected by the new alias. This behavior is also an issue when functions are executed. Aliases are expanded when a function definition is read, not when the function is executed, because a function definition is itself a compound command. As a consequence, aliases defined in a function are not available until after that function is executed. To be safe, always put alias definitions on a separate line, and do not use alias in compound commands.
I have a query regarding the execution of a complex command in the makefile of the current system.
I am currently using shell command in the makefile to execute the command. However my command fails as it is a combination of a many commands and execution collects a huge amount of data. The makefile content is something like this:
variable=$(shell ls -lart | grep name | cut -d/ -f2- )
However the make execution fails with execvp failure, since the file listing is huge and I need to parse all of them.
Please suggest me any ways to overcome this issue. Basically I would like to execute a complex command and assign that output to a makefile variable which I want to use later in the program.
(This may take a few iterations.)
This looks like a limitation of the architecture, not a Make limitation. There are several ways to address it, but you must show us how you use variable, otherwise even if you succeed in constructing it, you might not be able to use it as you intend. Please show us the exact operations you intend to perform on variable.
For now I suggest you do a couple of experiments and tell us the results. First, try the assignment with a short list of files (e.g. three) to verify that the assignment does what you intend. Second, in the directory with many files, try:
variable=$(shell ls -lart | grep name)
to see whether the problem is in grep or cut.
Rather than store the list of files in a variable you can easily use shell functionality to get the same result. It's a bit odd that you're flattening a recursive ls to only get the leaves, and then running mkdir -p which is really only useful if the parent directory doesn't exist, but if you know which depths you want to (for example the current directory and all subdirectories one level down) you can do something like this:
directories:
for path in ./*name* ./*/*name*; do \
mkdir "/some/path/$(basename "$path")" || exit 1; \
done
or even
find . -name '*name*' -exec mkdir "/some/path/$(basename {})" \;
When I use the command typeset -f in ksh, a list of functions with their definition is displayed in stdout.
I tried to search where those functions are defined, but I couldn't find any hint about them. Can anyone help me finding them?
EDIT
I'm just learning the use of the typeset command, typing man typeset game me nothing (no manual entry for typeset).
In order to define functions that will be displayed using typeset -f, we need to define a function and export it using typeset -xf.
Functions can be declared in the .profile, or files called from .profile or put in a dir that is referenced by the FPATH variable (and proabably other places too). Read your man ksh carefully for the order of files that are processed on startup. Search for the 'Invocation', 'Files', and 'Functions' sections.
Also, there are a group of default functions that ksh sets up. So please edit your question to show the function names that your concerned with.
IHTH
Shells don't keep a record of where functions (or aliases, or variables, etc...) are defined. Conceptually, and notwithstanding interactive usage features like shell history, shells read commands from input one at a time, execute them, and then forget them. Sometimes those commands come from interactive input, sometimes they come from scripts. Sometimes they have side effects like defining a function in the shell's environment, but the shell still doesn't remember the command or its position in the shell's input stream after it's finished executing it.
Is there a way in a unix shell (specifically Ubuntu) to change directory into the xth directory that was printed from the ls command?
I know you can sort a directory in multiple ways, but using the output from ls to get the xth directory?
An example shell:
$ ls
$ first_dir second_dir third_really_long_and_complex_dir
where I want to move into the third_really_long_and_complex_dir by passing 3 (or 2 in proper array format).
I know I could simply copy and paste, but if I'm already using the keyboard, it would be easier to type something like "cdls 2" or something like that if I knew the index.
The main problem with cd in an interactive session is that you generally want to change the current directory of the shell that is processing the command prompt. That means that launching a sub-shell (e.g. a script) would not help, since any cd calls would not affect the parent shell.
Depending on which shell you are using, however, you might be able to define a function to do this. For example in bash:
function cdls() {
# Save the current state of the nullglob option
SHOPT=`shopt -p nullglob`
# Make sure that */ expands to nothing when no directories are present
shopt -s nullglob
# Get a list of directories
DIRS=(*/)
# Restore the nullblob option state
$SHOPT
# cd using a zero-based index
cd "${DIRS[$1]}"
}
Note that in this example I absolutely refuse to parse the output of ls, for a number of reasons. Instead I let the shell itself retrieve a list of directories (or links to directories)...
That said, I suspect that using this function (or anything to this effect) is a very good way to set yourself up for an enormous mess - like using rm after changing to the wrong directory. File-name auto-completion is dangerous enough already, without forcing yourself to count...