vi highlights bash scripting uppercase or lowercase as error - bash

My vi has been wonderful in highlighting errors in my bash scripts. However, one thing that I notice is when I use the bash extension for all upppercase or all lowercase, vi highlights the characters as an error. Is there a solution for this?
#!/bin/bash
mystr="Abc"
echo ${mystr,,} # Works, but vi highlights ,, as in error
% ./test.sh
abc
%

VIM syntax highlighting is done via a Syntax file. Go into VIM and type in :echo $VIMRUNTIME. This will tell you the VIMRUNTIME directory. In that directory, there's a directory called syntax. Inside that are the syntax definitions. There's a file called sh.vim that contains the definitions for BASH, Kornshell, and Bourne shell.
Take a look around line #400 of the file, you'll see something like this:
" Special ${parameter OPERATOR word} handling: {{{1
" sh ksh bash : ${parameter:-word} word is default value
" sh ksh bash : ${parameter:=word} assign word as default value
" sh ksh bash : ${parameter:?word} display word if parameter is null
" sh ksh bash : ${parameter:+word} use word if parameter is not null, otherwise nothing
" ksh bash : ${parameter#pattern} remove small left pattern
" ksh bash : ${parameter##pattern} remove large left pattern
" ksh bash : ${parameter%pattern} remove small right pattern
" ksh bash : ${parameter%%pattern} remove large right pattern
It is in THIS section that defines the patterns and whether they're legal or not. You'll need to define a region that looks like this:
if exists("b:is_bash")
Here be dragons
endif
Actually, I see one farther down on line #423 in the latest version of the sh.vim file, so you don't have to define your own.
Now, all you have to do is replace the Here be dragons section with something that defines the syntax you want to show as not an error. I don't know enough of the VIM syntax to tell you. However, it doesn't look all that complicated. Backup the old sh.vim and have some fun. Believe it or not, it's all documented right in VIM. I've modified the syntax files for Perl and Python without too many issues.
you can also go to https://vim.svn.sourceforge.net/svnroot/vim/vim7/runtime/syntax/ and see if there's a new sh.vim file that might include the syntax fix you need.
You can also report the problem to the VIM project and let them know about the issue. They're pretty good about fixing things like this, and then you can go download the latest fix at the Subversion URL above.

Related

Sourcing a .sh file under zsh : addvar:1: bad substitution error message

I have the following .sh file that I would like to be able to source under zsh, which is my default shell. The beginning of the file is (after there are only export variable = path) :
function addvar () {
local tmp="${!1}" ;
tmp="${tmp//:${2}:/:}" ; tmp="${tmp/#${2}:/}" ; tmp="${tmp/%:${2}/}" ;
export $1="${2}:${tmp}" ;
}
At the execution : $ source file.sh
I get the following error message :
addvar:1: bad substitution
Could anyone see what's wrong ?
ps : I tried to put directly at the top : #!/bin/bash and execute directly by $ ./file.sh
but it doesn't do anything (none variables exported).
I have the following .sh file that I would like to be able to source under zsh, which is my default shell.
That filename extension suggests that the file is intended for a Bourne-family shell, but it says nothing about which one, and various members of the family have various incompatibilities with each other. If there is a shebang line (a line beginning with #! and appearing as the first line of the file) then it may give a more specific indication of the shell for which the file is intended, but that has functional relevance only when the file is executed directly, not when it is sourced.
I get the following error message :
addvar:1: bad substitution
Could anyone see what's wrong ?
The problem is with
local tmp="${!1}" ;
, and specifically with ${!1}. In Bash, this is an indirect variable reference, which expands to the value of the variable named by the expansion of ${1}. In Zsh, however, it is simply invalid, so you cannot use that code as-is in that shell.
It looks like the function is supposed to add the value specified as its second argument to the path-like value of the environment variable named by its first argument, removing any previous appearances of that value. Since you cannot use it as-is in Zsh, I would suggest just not. You might instead use a function that computes the appropriate updated value, and leaves the reassignment to the caller. Example:
add_path_element() {
local tmp="${1//:${2}:/:}"
tmp="${tmp/#${2}:/}"
tmp="${tmp/%:${2}/}"
echo -n "${tmp}:${2}"
}
# Instead of "addvar PATH /dir/to/add":
export PATH=$(add_path_element "$PATH" /dir/to/add)
It should also be possible to implement a version of the original addvar function that works in Zsh (and Bash) by use of the eval built-in, but I cannot recommend that approach to you because eval is very dangerous. I mention it here just in case someone else suggests it. As a basic rule, do not use eval until you are sufficiently expert at shell programming to thoroughly understand why you should not use eval.

How to make fish shell delete words like bash does

I'm giving a second try to the fish shell. One thing that really annoys me is the "new" behaviour of Ctrl+w shortcut.
Consider following situation:
$ vim ~/.config/fish/config.fish
...having the cursor at the end of the line.
When you press Ctrl+w, following happens:
in bash: ~/.config/fish/config.fish is deleted
in fish: only config.fish is deleted
How can I make fish delete words that are separated by spaces only?
"\cw" (in fish's notation) is bound to "backward-kill-path-component" (which bind \cw will tell you).
If you wish, you can bind it to something else, including input functions like "backward-kill-word" or any fish script - bind \cw backward-kill-word or bind \cw "commandline -rt ''" (which will remove the entire current token) or bind \cw backward-kill-bigword. See the bind documentation or bind --help for more information.
The difference between "word" and "bigword" here is that "word" will only go to the next non-word-character, which can be a "." or "/" or "-", among others, while "bigword" will truly go to the next whitespace character.
Note that the "bigword" functions have only been introduced in fish 2.3.0.
You can try these incantations in an interactive shell. If you decide to make it permanent, you'll need to add them to a function called fish_user_key_bindings.

How to input a comment on csh?

In bash, I used # to input comment. Even on interactive session.
bash-3.2$ #
bash-3.2$ #
bash-3.2$ #
bash-3.2$
csh spits error for this. How can I input some comment on interactive csh session? In other words, I am looking for a way to make 100% sure comment in csh.
root#freebsd9:~ # #
#: Command not found.
root#freebsd9:~ # # 3e
#: Command not found.
root#freebsd9:~ # #
#: Command not found.
root#freebsd9:~ #
Interactive csh or tcsh doesn't do comments. The # character introduces a comment only in a script. (This is unlike the behavior of sh and its derivatives, such as bash.) Quoting the csh man page (from Solaris 9, one of the remaining systems where csh is not just a symlink to tcsh):
When the shell's input is not a terminal, the character #
introduces a comment that continues to the end of the input line.
Its special meaning is suppressed when preceded by a \ or enclosed in
matching quotes.
The point, I think, is that interactive commands don't need comments.
If you're using tcsh, you can do something similar with the built-in : command, which does nothing:
% : 'This is not a comment, but it acts like one.'
(where % represents the shell prompt and : is the command). Quoting the argument is a good idea; otherwise, though the command is not executed, it can have some effect:
% : This will create the file "oops.txt" > oops.txt
Note that since : is a command, it must be followed by a space.
The : command was originally introduced in a very early version of the Bourne shell, or perhaps even before that.
However, the /bin/csh version of the : command does not permit any arguments, making it useless as a comment replacement:
csh% : 'This will not work.'
:: Too many arguments
csh%
(I didn't realize that when I initially posted this answer. I must have tested it with tcsh rather than a true csh.)
Since : doesn't work in pure csh, the next best solution is probably to use echo and redirect the output:
csh% echo 'This is not a comment, but it acts like one.' > /dev/null
Obligatory link: http://www.perl.com/doc/FMTEYEWTK/versus/csh.whynot
For the reference, I like to note my current workaround. It's using echo.
#!/bin/tcsh
echo "This is comment line."
echo "But still, beware... Because `expression` is still being evaluated.
This is the best way I could find.
To use # for interactive comments in tcsh or csh, this seems to work in most cases:
alias '#' 'echo \!* >> /dev/null'
It can be run by the user interactively or placed in a .tcshrc or .cshrc configuration file as appropriate.
(The alias can obviously be named differently as you might desire; normal restrictions apply.)
Note: as Keith Thompson noted above, this will still give errors if your comment includes redirect characters such as > or >>, but used this way does not appear to actually create an undesired redirect file. Surrounding the comment in single quotes (') is still a workaround.
Check if your script has UNIX EOL Format. Has some problem with Windows EOL Format.

vim red highlight $( )

I was writing a script when I decided to move the functions to a lib file, but when I open the lib file all the $( and the consecutive ) are red highlighted, here are some examples of the script
TAB="$(printf '\t')"
percent=$(echo "scale=2; $number/$total*100" | bc | sed -e 's/\.[[:digit:]]*//g')
if [[ -z $(grep $site/post $max_lim) ]];then
The filetype is conf but I've set it as sh syntax in .vimrc
Any idea of what is happenning?
Thank you
Edit: Thanks for the quick answers, I found that this line makes vim match the files with the extension specified behind the * with the syntax sh
au BufReadPost * set syntax=sh
I've also thought that using shebang in the libraries was not allowed, but is a nice solution
Anyway using g:is_bash in .vimrc returns an error of pattern not found
So what I would like to do is as I only write in bash, to vim recognize any file without extension as bash
The syntax file for sh actually handles several different kinds of shell syntax: bash, ksh, and plain old sh. Since your conf file isn't recognized as bash or ksh, it falls back to sh. $(...) isn't a valid construct in sh, so it is highlighted as an error.
To fix this, you can make sure "g:is_bash" is set for the file, so that the sh syntax script will know your file should be highlighted as bash code. Please edit your question to include what you added to your .vimrc to make the file use sh syntax highlighting. This will make it easier to suggest the correct way of setting "g:is_bash".
UPDATE: As Alok commented, you should be able to add the following to the file
#!/bin/bash
to let vim know the correct syntax highlighting to use as well.
In my case, I wanted to preserve #!/bin/sh as the shebang line because not every system has /bin/bash available.
Whereas the original Bourne shell may have not supported the $(...) syntax, most sh shells nowadays are POSIX-compliant, and the POSIX spec supports this syntax. For example,
On Ubuntu, /bin/sh is /bin/dash.
On MacOS, /bin/sh is /bin/bash.
On Alpine, /bin/sh is /bin/ash.
All of which satisfy the POSIX spec. Traditionally, if we'd like to write portable Shell, we should leave the shebang line as #!/bin/sh. We shouldn't change it to #!/bin/bash just for syntax highlighting if we're not going to use any Bashisms.
Okay, but what about the erroneous red highlighting? The problem is with Vim interpreting #!/bin/sh as a reference to the original Bourne shell from 1979 with no support for $(...). Maybe this is a testament to Vim's backwards compatibility, or maybe not enough people care. Here's a related GitHub issue describing the same behavior.
In any case, the best solution for me was to set let g:is_posix = 1 in my config. Interestingly, if you look through Vim's runtime files, it's equivalent to setting let g:is_kornshell = 1.
A brief interesting history on how the Bourne shell was bourne, bourne again as bash as a substitute for /bin/sh on Ubuntu, and eventually replaced in favor of dash can be found at https://askubuntu.com/a/976504.

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

Resources