Broken tab completion on make under linux - linux

I have no idea how tab completion works, but all of a sudden mine is broken. I don't even know what info to provide other than the use case.
there is a target clean in the makefile.
$ make c<tab> results in
$ make c23:set: command not found
lean
EDIT:
I believe somehow I ruined the set bash built-in since man set says No manual entry for set and which set doesn't report anything. Invoking set on the terminal, however, produces result.
I'm using: GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu) and GNU Make 3.81

thanks to Etan's comment and Aaron's indication of where makefiles are, I managed to debug this.
I ran set -x so I could track what was happening when doing the tab completion. The output of make c<tab> consists mostly of commands from the bash completion file for make, located at /usr/share/bash-completion/completions/make (1).
However, I noticed the an inconsistency between the output and the file. Towards the end, the output said:
+ local mode=--
+ (( COMP_TYPE != 9 ))
++ set +o
++ grep --colour=auto -n -F posix
+ local 'reset=23:set +o posix'
+ set +o posix
Which I identified as corresponding to these lines from the file:
if (( COMP_TYPE != 9 )); then
mode=-d # display-only mode
fi
local reset=$( set +o | grep -F posix ); set +o posix # for <(...)
So the output did a grep --colour=auto -n instead of just grep. Indeed, I had setup this alias for grep
Make worked as soon as I removed the alias.
I hope this helps others debug their problems.
EDIT: I have submitted a bug report here: https://alioth.debian.org/tracker/index.php?func=detail&aid=315108&group_id=100114&atid=413095

Look into /etc/bash_completion, /etc/bash_completion.d and/or /usr/share/bash-completion/completions. You should find a file make which contains the script that will be called when press Tab.
Use the packaging system of your Linux distro to validate the file (or maybe revert to an older version).
Another cause of this could be something in the Makefile which throws the parser in the BASH completion script off the track.

Not trying to get any credits here, but the best solution is actually a bit hidden in the comments...
Please vote this comment up instead of my answer!
easy steps to fix this:
sudo vi /usr/share/bash-completion/completions/make
find the line that has the grep instruction. It should look like this:
local reset=$( set +o | grep -F posix ); set +o posix # for <(...)
add a "\" before the "grep" instruction:
local reset=$( set +o | \grep -F posix ); set +o posix # for <(...)

Related

ZSH on Linux doesn't recognize valid options in a shell script

zsh doesn't recognize options set with -o, but only when its in a shebang, and on Linux.
The following script fails on zsh 5.0.2, zsh 5.6, and the latest git:
#!/bin/zsh -o pipefail
thiswillfail | echo 'hello, world'
echo $?
exit
Expected Output
hello, world
/home/prajjwal/script:2: command not found: thiswillfail
127
Actual Output
/bin/zsh: no such option: pipefail
What works
The script on zsh 5.3 on MacOS Mojave. This appears to be failing on every Linux version I've tried so far, though.
Manually invoking /bin/zsh -o pipefail on a terminal
Setting the option with set -o pipefail after the shebang in the script.
What I've tried
Emptying my .zshrc's to ensure one of my settings isn't causing this.
Aside
While I'm only trying to get pipefail to work, this refuses to work with any other options that I try to set, even though all of them are mentioned in zshoptions.
This is a pitfall of trying to set options on the #! line. The kernel only splits the shebang line at the first space.
When you say
#!/bin/zsh -o pipefail
the command that ends up being executed is "/bin/zsh" "-o pipefail" "/home/prajjwal/script".
The error message says that " pipefail" (note the leading space) is not a valid option, which is correct: Only "pipefail" is valid.
In this case a possible workaround is to cram everything into a single command line argument:
#!/bin/zsh -opipefail
But in general that's not possible and the #! interface doesn't let you pass more than one extra argument, so your choices are to find other workarounds (e.g. using set manually) or to write a wrapper script:
#!/bin/zsh
exec /bin/zsh -o pipefail /path/to/the/real/script
On some systems another alternative is available: env -S. This option is not specified by POSIX and is not available on all systems. It works on systems using the GNU tools (including all standard Linux distributions) and FreeBSD, but not OpenBSD or NetBSD. I couldn't find any information about MacOS.
(Note: GNU env also supports --split-string as an alternative (long) option name, but FreeBSD env does not.)
Usage:
#!/usr/bin/env -S /bin/zsh -o pipefail
This invokes env with a single long argument ('-S /bin/zsh -o pipefail'). Standard option processing treats it as the -S option followed by an argument (' /bin/zsh -o pipefail').
In a simple case like this, env -S splits the argument on spaces/tabs and treats the resulting list as if it had been part of the original command line in the first place:
env -S ' /bin/zsh -o pipefail'
# works the same as:
env /bin/zsh -o pipefail
In less simple cases you'll have to quote some characters (env -S treats spaces, ", ', \, $ specially, among others). In particular, it does not work like shell quoting. For details refer to the manual pages linked above.

How to pipe help results to less by default?

I use "--help" or "-h" a lot. By default it uses cat to open help file. How can i set it to use less by default. I want the result of "command --help |less" to be the same as "command --help"
An alias for --help wouldn't work. Aliases only apply to command names, not to their arguments. I wouldn't recommend a completely invisible solution, anyways. Too much command-line magic can create bad habits.
You could create a help command instead.
h() { "$#" --help | less; }
$ h cat
Usage: cat [OPTION]... [FILE]...
<snip>
Commands to not default to cat they just write to standard output.
Some comments (e.g., man) send the output to the application specified by the PAGER variable. You can set it to your favourite pager
export PAGER=less
But this will only work for applications that actually support it.
Otherwise you will have to pipe your commands as in your question.

How to parse but not execute it? [duplicate]

Is it possible to check a bash script syntax without executing it?
Using Perl, I can run perl -c 'script name'. Is there any equivalent command for bash scripts?
bash -n scriptname
Perhaps an obvious caveat: this validates syntax but won't check if your bash script tries to execute a command that isn't in your path, like ech hello instead of echo hello.
Time changes everything. Here is a web site which provide online syntax checking for shell script.
I found it is very powerful detecting common errors.
About ShellCheck
ShellCheck is a static analysis and linting tool for sh/bash scripts. It's mainly focused on handling typical beginner and intermediate level syntax errors and pitfalls where the shell just gives a cryptic error message or strange behavior, but it also reports on a few more advanced issues where corner cases can cause delayed failures.
Haskell source code is available on GitHub!
I also enable the 'u' option on every bash script I write in order to do some extra checking:
set -u
This will report the usage of uninitialized variables, like in the following script 'check_init.sh'
#!/bin/sh
set -u
message=hello
echo $mesage
Running the script :
$ check_init.sh
Will report the following :
./check_init.sh[4]: mesage: Parameter not set.
Very useful to catch typos
sh -n script-name
Run this. If there are any syntax errors in the script, then it returns the same error message.
If there are no errors, then it comes out without giving any message. You can check immediately by using echo $?, which will return 0 confirming successful without any mistake.
It worked for me well. I ran on Linux OS, Bash Shell.
I actually check all bash scripts in current dir for syntax errors WITHOUT running them using find tool:
Example:
find . -name '*.sh' -print0 | xargs -0 -P"$(nproc)" -I{} bash -n "{}"
If you want to use it for a single file, just edit the wildcard with the name of the file.
null command [colon] also useful when debugging to see variable's value
set -x
for i in {1..10}; do
let i=i+1
: i=$i
done
set -
For only validating syntax:
shellcheck [programPath]
For running the program only if syntax passes, so debugging both syntax and execution:
shellproof [programPath]
Bash shell scripts will run a syntax check if you enable syntax checking with
set -o noexec
if you want to turn off syntax checking
set +o noexec
There is BashSupport plugin for IntelliJ IDEA which checks the syntax.
If you need in a variable the validity of all the files in a directory (git pre-commit hook, build lint script), you can catch the stderr output of the "sh -n" or "bash -n" commands (see other answers) in a variable, and have a "if/else" based on that
bashErrLines=$(find bin/ -type f -name '*.sh' -exec sh -n {} \; 2>&1 > /dev/null)
if [ "$bashErrLines" != "" ]; then
# at least one sh file in the bin dir has a syntax error
echo $bashErrLines;
exit;
fi
Change "sh" with "bash" depending on your needs

Color termcaps Konsole?

I've got a problem with ANSI escape codes in my terminal on OpenSuse 13.2.
My Makefile use to display pretty colors on OSX at work but at home when I use it I get the litteral termcaps such as \033[1;30m ... \033[0m
I know close to nothing about termcaps, I just found these escape characters that seemed to be working fine ! The strangest is that both my OSX and Linux terminal are configured with TERM=xterm-256color so I really don't know where to look for the correct setting I'm currently missing on Linux.
TL;DR: How to get escape codes such as \033[1;30m working in Konsole with xterm-256color ?
Edit: Here's a snippet of the Makefile I am talking about:
\Here's a snippet of the Makefile I am talking about:
# Display settings
RED_L = \033[1;31m
GREEN_L = \033[1;32m
GREEN = \033[0;32m
BLUE = \033[0;34m
RED = \033[0;31m
all: $(OBJ_DIR) $(NAME)
$(OBJ_DIR):
#mkdir -p $(OBJ_DIR)
$(NAME): $(OBJ)
#echo "$(BLUE)Linking binary $(RED)$(NAME)$(BLUE).\n"
#$(CC) -o $# $^ $(LFLAGS)
#echo "\t✻ $(GRAY)$(CC) -o $(RED)$(NAME)$(GRAY) object files:$(GREEN) OK! √\n$(NC)
The example which you gave does not rely upon the setting of TERM (unless it is going someplace other than the terminal, e.g., via some program which interprets it such as the ls program, which has its own notion about colors). It would help if you quoted the section of the makefile which uses the escape sequences. Without that, we can offer only generic advice, e.g,. by assuming you have an echo command in the makefile.
The place to start looking is at the shell which your makefile uses. One would expect bash to be the default shell on OpenSUSE. But suppose you are actually using some other shell which happens to not recognize the syntax you are using, and trying to do something like
echo '\033[1;34mhello\033[m'
To help ensure that you are using the expected shell, you can put an assignment in your makefile, e.g.,
SHELL = /bin/sh
This assumes that /bin/sh itself is going to work as intended. However, that is commonly a symbolic link (for Linux) to the real shell. If so, one possible solution would be to change the real shell using OpenSUSE's update-alternatives feature to change the shell to bash (or zsh).
For additional information, see the discussion of SHELL in the GNU make manual.
Reflecting comments on the version of make -- GNU make 4.0 is known to have incompatible changes versus 3.81, as noted in the thread GNU Make 4.0 released on LWN.net. In particular, there are several comments relating to your problem, starting here.
However, checking a recent Fedora, it seems that the problem really is that the default behavior for echo has changed. As noted in other discussions (such as Why doesn't echo support “\e” (escape) when using the -e argument in MacOSX), this was done to improve POSIX compatibility. You can get your colors back by adding a -e option to the echo commands.
I finally found the solution:
the problem was I used echo instead of echo -e which seems to be the default behaivour on Mac OSX.
Thanks for your help though, it lead me to good lectures :)

How do I syntax check a Bash script without running it?

Is it possible to check a bash script syntax without executing it?
Using Perl, I can run perl -c 'script name'. Is there any equivalent command for bash scripts?
bash -n scriptname
Perhaps an obvious caveat: this validates syntax but won't check if your bash script tries to execute a command that isn't in your path, like ech hello instead of echo hello.
Time changes everything. Here is a web site which provide online syntax checking for shell script.
I found it is very powerful detecting common errors.
About ShellCheck
ShellCheck is a static analysis and linting tool for sh/bash scripts. It's mainly focused on handling typical beginner and intermediate level syntax errors and pitfalls where the shell just gives a cryptic error message or strange behavior, but it also reports on a few more advanced issues where corner cases can cause delayed failures.
Haskell source code is available on GitHub!
I also enable the 'u' option on every bash script I write in order to do some extra checking:
set -u
This will report the usage of uninitialized variables, like in the following script 'check_init.sh'
#!/bin/sh
set -u
message=hello
echo $mesage
Running the script :
$ check_init.sh
Will report the following :
./check_init.sh[4]: mesage: Parameter not set.
Very useful to catch typos
sh -n script-name
Run this. If there are any syntax errors in the script, then it returns the same error message.
If there are no errors, then it comes out without giving any message. You can check immediately by using echo $?, which will return 0 confirming successful without any mistake.
It worked for me well. I ran on Linux OS, Bash Shell.
I actually check all bash scripts in current dir for syntax errors WITHOUT running them using find tool:
Example:
find . -name '*.sh' -print0 | xargs -0 -P"$(nproc)" -I{} bash -n "{}"
If you want to use it for a single file, just edit the wildcard with the name of the file.
null command [colon] also useful when debugging to see variable's value
set -x
for i in {1..10}; do
let i=i+1
: i=$i
done
set -
For only validating syntax:
shellcheck [programPath]
For running the program only if syntax passes, so debugging both syntax and execution:
shellproof [programPath]
Bash shell scripts will run a syntax check if you enable syntax checking with
set -o noexec
if you want to turn off syntax checking
set +o noexec
There is BashSupport plugin for IntelliJ IDEA which checks the syntax.
If you need in a variable the validity of all the files in a directory (git pre-commit hook, build lint script), you can catch the stderr output of the "sh -n" or "bash -n" commands (see other answers) in a variable, and have a "if/else" based on that
bashErrLines=$(find bin/ -type f -name '*.sh' -exec sh -n {} \; 2>&1 > /dev/null)
if [ "$bashErrLines" != "" ]; then
# at least one sh file in the bin dir has a syntax error
echo $bashErrLines;
exit;
fi
Change "sh" with "bash" depending on your needs

Resources