what does this strange little yellow arrow below the command prompt mean? - bash

I am installing rbenv, following their documentation to
add ~/.rbenv/bin to your $PATH for access to the rbenv command-line utility. And this arrow shows up. What does it mean?

It's almost certainly your PS2 prompt, used for line continuation. For example, I have PS1 set to 'pax> ' and PS2 set to '...> ' and the following transcript shows that in action:
pax> echo 'hello
...> there'
hello
there
The reason the PS2 prompt is there is because you have an unterminated " character (though, technically, this is an unstarted quote but the shell isn't smart enough to realise that and the effect is the same).
I suggest you try something like:
echo 'export PATH=$HOME/.rbenv/bin:$PATH' >>~/.bash_profile
You'll notice I'm using single quotes there as well since you probably want the literal strings $HOME and $PATH being placed in your profile rather than their current interpreted values.
And, one other thing, there are certain classes of profiles that will "break" if you just blindly append stuff to the end.
It's fine for profiles that are simple sequential beasts (ones that run cleanly from top to bottom) but, if you have something with a lot of control structures (like if statements), you should be investigating the best place to put it in the file.
By that I mean a profile containing (the admittedly contrived):
# Don't do rest of file in first two weeks of a month.
if [[ $(date +%d) -le 14 ]]; then
exit
fi
may incorrectly skip your changes if you just add them to the file end without analysis. In this case, the best place is probably immediately above that if statement.

Related

Bash script: any way to collect remainder of command line as a string, including quote characters?

The following simplified version of a script I'll call logit obviously just appends everything but $1 in a text file, so I can keep track of time like this:
$ logit Started work on default theme
But bash expansion gets confused by quotes of any kind. What I'd like is to do things like
$ logit Don't forget a dark mode
But when that happens of course shell expansion rules cause a burp:
quote>
I know this works:
# Yeah yeah I can enclose it in quotes but I'd prefer not to
$ logit "Don't forget a dark mode"
Is there any way to somehow collect the remainder of the command line before bash gets to it, without having to use quotes around my command line?
Here's a minimal working version of the script.
#!/bin/bash
log_file=~/log.txt
now=$(date +"%T %r")
echo "${now} ${#:1}" >> $log_file
Is there any way to somehow collect the remainder of the command line before bash gets to it, without having to use quotes around my command line?
No. There is no "before bash gets into it" time. Bash reads the input you are typing, Bash parses the input you are typing, there is nothing in between or "before". There is only Bash.
You can: use a different shell or write your own. Note that quotes parsing like in shell is very common, you may consider that it could be better for you to understand and get used to it.
you can use a backslash "\" before the single quote
$ logit Don\'t forget a dark mode

Bash: How to set changing variable in PS1, updating every prompt

I've searched for quite a while, but haven't been able to find a post or any information whatsoever about adding a variable that is, well, variable (i.e. changing) within PS1 in bash, that will update every time a new prompt occurs. In concreto, I would like the width of the prompt to span across the whole terminal window, e.g.:
7zS2::awesome| --------------------------------------------------------- ~/.config/awesome
This is what I have so far, omitting color codes for legibility:
mytest=$PWD
mynext="$(basename $PWD)"
mylength=$((${#mytest}+${#mynext}))
length=$(($mylength+6))
PS1='7zS2::\W| $(printf "\\u2500%.0s" $(seq $length $(tput cols))) \w\n\$'
Which works perfectly whenever I
exec bash
to reset the prompt.
However, I would like it to work without me having to reload bash. Surely there must be a way of doing this, as \w, \W and of the likes are unique and updated every prompt as well. Any way of easily doing this?
Thanks!
7zS2
Try this. Place your code into a file, I'm calling it gash.sh in my home directory, with a small modification: replace the PS1 assignment with echo, and replace the single quotes with double.
mytest=$PWD
mynext="$(basename $PWD)"
mylength=$((${#mytest}+${#mynext}))
length=$(($mylength+6))
echo "7zS2::\W| $(printf \\u2500%.0s $(seq $length $(tput cols))) \w\n\$"
then in your startup file set PS1:
PS1='$(~/gash.sh)'

ZSH script and prompt profiling?

This answer, "How to profile a bash shell script?", seems to nearly perfectly cover what I'm trying to accomplish here. I currently have some zsh scripts that modify the prompt, however I think some updates to oh-my-zsh have evoked some issues that I need to hunt down. The sluggishness from time to time is unbearable.
To this end, how would you adapt the prompt sections in this example answer to work with zsh vs bash?
Presently I have modified /etc/zshenv such that it has the initial suggested code from the example:
PS4='+ $(date "+%s.%N")\011 '
exec 3>&2 2>/tmp/bashstart.$$.log
set -x
And my ~/.zshrc has the following appended to it's tail:
set +x
exec 2>&3 3>&-
Of course these are not valid for ZSH shell customization. My prompt rendering code utilizes oh-my-zsh customizations. I could prepend the appropriate code to the prompt I suppose or I'm open to other suggestions.
Calling date for each command will fork and exec, which adds overhead which may interfere with your measurements.
Instead, you could use
PS4=$'+ %D{%s.%6.}\011 '
to log timestamps with lower overhead (up to millisecond precision).
For some notes on processing the resulting logs, see http://blog.xebia.com/profiling-zsh-shell-scripts/
You may need to do
setopt prompt_subst
if it's not already.
Also, in order to interpret the octal escape for tab, use $'':
PS4=$'+ $(date "+%s.%N")\011 '
You may also find some of these escapes to be useful:
%? The return status of the last command executed just before the prompt.
%_ The status of the parser, i.e. the shell constructs (like `if' and `for') that have been started on the command
line. If given an integer number that many strings will be printed; zero or negative or no integer means print as
many as there are. This is most useful in prompts PS2 for continuation lines and PS4 for debugging with the
XTRACE option; in the latter case it will also work non-interactively.
%i The line number currently being executed in the script, sourced file, or shell function given by %N. This is most
useful for debugging as part of $PS4.
%I The line number currently being executed in the file %x. This is similar to %i, but the line number is always a
line number in the file where the code was defined, even if the code is a shell function.
%L The current value of $SHLVL.
%N The name of the script, sourced file, or shell function that zsh is currently executing, whichever was started
most recently. If there is none, this is equivalent to the parameter $0. An integer may follow the `%' to spec‐
ify a number of trailing path components to show; zero means the full path. A negative integer specifies leading
components.
%x The name of the file containing the source code currently being executed. This behaves as %N except that function
and eval command names are not shown, instead the file where they were defined.

ZSH/Shell variable assignment/usage

I use ZSH for my terminal shell, and whilst I've written several functions to automate specific tasks, I've never really attempted anything that requires the functionality I'm after at the moment.
I've recently re-written a blog using Jekyll and I want to automate the production of blog posts and finally the uploading of the newly produced files to my server using something like scp.
I'm slightly confused about the variable bindings/usage in ZSH; for example:
DATE= date +'20%y-%m-%d'
echo $DATE
correctly outputs 2011-08-23 as I'd expect.
But when I try:
DATE= date +'20%y-%m-%d'
FILE= "~/path/to/_posts/$DATE-$1.markdown"
echo $FILE
It outputs:
2011-08-23
blog.sh: line 4: ~/path/to/_posts/-.markdown: No such file or directory
And when run with what I'd be wanting the blog title to be (ignoring the fact the string needs to be manipulated to make it more url friendly and that the route path/to doesn't exist)
i.e. blog "blog title", outputs:
2011-08-23
blog.sh: line 4: ~/path/to/_posts/-blog title.markdown: No such file or directory
Why is $DATE printing above the call to print $FILE rather than the string being included in $FILE?
Two things are going wrong here.
Firstly, your first snippet is not doing what I think you think it is. Try removing the second line, the echo. It still prints the date, right? Because this:
DATE= date +'20%y-%m-%d'
Is not a variable assignment - it's an invocation of date with an auxiliary environment variable (the general syntax is VAR_NAME=VAR_VALUE COMMAND). You mean this:
DATE=$(date +'20%y-%m-%d')
Your second snippet will still fail, but differently. Again, you're using the invoke-with-environment syntax instead of assignment. You mean:
# note the lack of a space after the equals sign
FILE="~/path/to/_posts/$DATE-$1.markdown"
I think that should do the trick.
Disclaimer
While I know bash very well, I only started using zsh recently; there may be zshisms at work here that I'm not aware of.
Learn about what a shell calls 'expansion'. There are several kinds, performed in a particular order:
The order of word expansion is as follows:
tilde expansion
parameter expansion
command substitution
arithmetic expansion
pathname expansion, unless set -f is in effect
quote removal, always performed last
Note that tilde expansion is only performed when the tilde is not quoted; viz.:
$ FILE="~/.zshrc"
$ echo $FILE
~/.zshrc
$ FILE=~./zshrc
$ echo $FILE
/home/user42/.zshrc
And there must be no spaces around the = in variable assignments.
Since you asked in a comment where to learn shell programming, there are several options:
Read the shell's manual page man zsh
Read the specification of the POSIX shell, http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html, especially if you want to run your scripts on different operating systems (and you will find yourself in that situation one fine day!)
Read books about shell programming.
Hang out in the usenet newsgroup comp.unix.shell where a lot of shell wizards answer questions

KornShell (ksh) wraparound

Okay, I am sure this is simple but it is driving me nuts. I recently went to work on a program where I have had to step back in time a bit and use Redhat 9. When I'm typing on the command line from a standard xterm running KornShell (ksh), and I reach the end of the line the screen slides to the right (cutting off the left side of my command) instead of wrapping the text around to a new line. This makes things difficult for me because I can't easily copy and paste from the previous command straight from the command line. I have to look at the history and paste the command from there. In case you are wondering, I do a lot of command-line awk scripts that cause the line to get quite long.
Is there a way to force the command line to wrap instead of shifting visibility to the right side of the command I am typing?
I have poured through man page options with no luck.
I'm running:
XFree86 4.2.99.903(174)
KSH 5.2.14.
Thanks.
Did you do man ksh?
You want to do a set -o multiline.
Excerpt from man ksh:
multiline:
The built-in editors will use multiple lines on the screen for
lines that are longer than the width of the screen. This may not
work for all terminals.
eval $(resize) should do it.
If possible, try to break the command down to multiple lines by adding \
ie:
$ mycommand -a foo \
-f bar \
-c dif
The simple answer is:
$ set -o multiline
ksh earlier than 5.12, like the ksh shipped with NetBSD 6.1, doesn't have this option. You will have to turn off current Interactive Input Line Editing mode, which is usually emacs:
$ set +o emacs
This turns off a lot of featuers altogether, like tab-completion or the use of 'Up-arrow' key to roll back the previous command.
If you decide to get used to emacs mode somehow, remember ^a goes to the begining of the line ("Home" key won't workk) and ^e goes to the end.
I don't know of a way of forcing the shell to wrap, but I would ask why you'd be writing lines that long. With awk scripts, I simply wrap the script in single quotes, and then break the lines where I want. It only gets tricky if you need single quotes in the script -- and diabolical if you need both single and double quotes. Actually, the rule is simple enough: use single quotes to wrap the whole script, and when you want a single quote in the script, write '\''. The first quote terminates the previous single-quoted string; the backslash-single quote yields a single quote; and the last single quote starts a new single quoted string. It really gets hairy if you need to escape those characters for an eval or something similar.
The other question is - why not launch into an editor. Since I'm a die-hard vim nutcase (ok - I've been using vi for over 20 years, so it is easier for me than the alternatives), I have Korn shell set to vi mode (set -o vi), and can do escape-v to launch the editor on whatever I've typed.
This is kind of a pragmatic answer, but when that's an issue for me I usually do something like:
strings ~/.history | grep COMMAND
or
strings ~/.history | tail
(The history file has a little bit of binary data in it, hence 'strings')

Resources