Bash: SPACE-triggered completion - bash

In bash, I would like to achieve the following workflow (prompt shown in brackets, _ is the cursor):
Type "foo" ($ foo_)
Press space. At this point:
if foo is a function, say function foo() { printf "hello from foo" }
space is printed ($ foo _)
the function is called and hello from foo is printed after a space - no newline ($ foo hello from foo_)
if foo is not a function, space is printed and that's it ($ foo _)
I have tried:
Making space send 0x0a (return) to the terminal emulator (iTerm2 on Mac). This works except it obviously prints the newline character as well
Using the built-in complete function: complete -F foo -o nospace foo. This works but I have to type foo then SPACE then TAB for hello from foo to be printed inline
I have heard you could somehow embed a \n-eating script into PS1. Really don't know how to get started on that one.
I could also trap a shortcut, such as Ctrl+T, to execute foo - but I'd really like to only press SPACE.
Ideally space would behave like this only for the first word being typed into the terminal. Any help would be appreciated, please save my sanity.
Why in the world I need this: (I'm a geek AND) I've got an emacs-like ido script that I'd like to be invoked when I type cd followed by SPACE.

A way to do this is using -n 1 with read. For example:
foo(){
echo hello from foo
}
string=''
while read -n 1 a ; do
if [ "$a" = "" ] ; then
tpe=`type -t $string`
if [ "$tpe" = "function" ] ; then
$string
fi
string=''
else
string="$string$a"
fi
done

Related

How to use a single bash command like $1 or $2 or $3 as a string of text instead of one word?

How can I use a bashrc command like below
# .bashrc file
# google_speech
say () {
google_speech -l en "$1"
}
as a string of text, since the above code only reads out the first word of the sentence or paragraph i paste.
like for example if i go into terminal and type:
$ say hello friends how are you
then the script only thinks i typed
$ say hello
Try to use "$#" (with double quotes arround) to get all the arguments of your function:
$ declare -f mysearch # Showing the definition of mysearch (provided by your .bashrc)
mysearch ()
{
echo "Searching with keywords : $#"
}
$ mysearch foo bar # execution result
Searching with keywords : foo bar
Function or scripts arguments are like arrays, so you can use:
1) $# / ${#array[#]} to getting the number of arguments / array's elements.
2) $1 / ${array[1]} to getting the first argument / array's element.
3) $# / ${array[#]} to getting all arguments / array's elements.
EDIT: according to chepner's comment:
Using $# inside a larger string can sometimes produce unintended results. Here, the intent is to produce a single word, so it would be better to use $*
And here's a good topic with great answers explaining the differences.
EDIT 2: Do not forget to put double quotes around $# or $*, maybe your google_speach is taking only one arg. Here's a demo to give you a better understanding:
$ mysearch () { echo "Searching with keywords : $1"; }
$ mysearch2 () { mysearch "$*"; }
$ mysearch2 Hello my dear
Searching with keywords : Hello my dear
$ mysearch3 () { mysearch $*; } # missing double quotes
$ mysearch3 Hello my dear
Searching with keywords : Hello # the other arguments are at least ignored (or sometimes the program will fail when he's controlling the args).

Print a string with its special characters printed as literal escape sequences

I have a string in a shell/bash script. I want to print the string with all its "special characters" (eg. newlines, tabs, etc.) printed as literal escape sequences (eg. a newline is printed as \n, a tab is printed as \t, and so on).
(Not sure if I'm using the correct terminology; the example should hopefully clarify things.)
Example
The desired output of...
a="foo\t\tbar"
b="foo bar"
print_escape_seq "$a"
print_escape_seq "$b"
...is:
foo\t\tbar
foo\t\tbar
$a and $b are strings that were read in from a text file.
There are two tab characters between foo and bar in the $b variable.
An attempt
This is what I've tried:
#!/bin/sh
print_escape_seq() {
str=$(printf "%q\n" $1)
str=${str/\/\//\/}
echo $str
}
a="foo\t\tbar"
b="foo bar"
print_escape_seq "$a"
print_escape_seq "$b"
The output is:
foo\t\tbar
foo bar
So, it doesn't work for $b.
Is there an entirely straightforward way to accomplish this that I've missed completely?
Bash has a string quoting operation ${var#Q}
Here is some example code
bash_encode () {
esc=${1#Q}
echo "${esc:2:-1}"
}
testval=$(printf "hello\t\tworld")
set | grep "^testval="
echo "The encoded value of testval is" $(bash_encode "$testval")
Here is the output
testval=$'hello\t\tworld'
The encoded value of testval is hello\t\tworld
You will need to create a search and replace pattern for each binary value you wish to replace. Something like this:
#!/bin/bash
esc() {
# space char after //
v=${1// /\\s}
# tab character after //
v=${v// /\\t}
echo $v
}
esc "hello world"
esc "hello world"
This outputs
hello\sworld
hello\tworld
I required something similar for file paths, and I realized that ls -1b does the work, but in the research I found this solution in stackoverflow which is closer to what you were requiring.
Command to escape a string in bash
just compile it with gcc -o "escapify" escapify.c

Can bash autocompletion span an equal sign

I'm wondering if it's possible to make autocompletion span an = sign. So, for example, I want to type foo BAR=[TAB][TAB], and have it fill in the possible values for BAR. I've tried the following: I have a file called 'bar', as follows:
#!/bin/bash
echo -e "BAR=100\nBAR=110\nBAR=200" | grep "^$2"
And then I do:
~> complete -C bar foo
If I type foo [TAB][TAB], it gives me some possible values for BAR. If, I type foo BAR=[TAB][TAB], it fails (it appends BAR=BAR= to the end of the command). (note, if I type bar 1 BAR=, it gives me a proper list of completions, so this is not an issue with the bar script).
This would be very useful for some scripts I have.
Create a function (in your .bashrc e.g.):
bar()
{
local POS=${COMP_WORDS[COMP_CWORD]}
if [ "${COMP_WORDS[1]}" = "BAR" ] && [ $COMP_CWORD -eq 3 ]; then
COMPREPLY=($(echo -e "100\n110\n200" | grep ^$POS ))
fi
}
and link function to command foo:
complete -F bar foo

Weird cat behaviour

inside of a function, I would have had multiple echos, but I remembered I could do cat << EOF followed by my text to output, followed by EOF. However, it only seems to work when the EOF part is not indented, like this
init(){
if [ conditionial ]; then
cat << EOF
this is some text
this is more text
this is even more text
EOF
but it doesn't work like this:
init(){
if [ conditionial ]; then
cat << EOF
this is some text
this is more text
this is even more text
EOF
Any ideas?
"Here document" terminators must appear at the beginning of the line, unless the redirection operator has a - suffix:
cat << END
this does not
END
here, but rather here
END
vs:
cat <<- END
this one does end here
END
(watch out for spaces vs tabs; and some older shell variants do not support this).
Besides these, another useful trick is the handling of expansions inside here documents. If the end word (END in my examples above, EOF in yours) is quoted, expansion is inhibited:
cat << E1
you are in a maze of twisty little directories: $PWD
E1
cat << 'E2'
you owe the Usenet Oracle $1.75 for the Tab.
E2
I never knew this till you asked, but you can use <<- (note the -) to ignore leading tabs (not spaces).
See here
The EOF has to in the beginning of the line and It cant't have anything after it but end of line:
#! /bin/bash
function say_hello {
cat << EOF
hello
world
EOF
}
Execution:
bash test.sh
hello
world
Taking a closer look with cat -A:
cat -A test.sh
#! /bin/bash$
$
function say_hello {$
^Icat << EOF$
^Ihello$
^Iworld$
EOF$
}$
$
say_hello$
I had problem with script which would have space after EOF and would not work, only with cat -A I could identify the space.

Line error in shell script

I have the following code in a shell script.
This only seems to work when it is not defined in a function.
The problematic line is the one containing the "<<".
The error message is
"./run: line 210: syntax error:
unexpected end of file"
How can I write this correctly within a function?
init_database()
{
cd ../cfg
db.sh << ENDC
$DB_ADMIN
0
y
n
ENDC
check_status
sqlplus $DB_SCHEMA#$DB_NAME < initial_data.sql
cd -
}
There are a number of ways to fix that problem.
1/ Unindent the here document end marker, such as:
cat <<EOF
hello
$PWD
EOF
but that will make your code look ugly.
2/ "Indent" the here document begin marker:
cat <<' EOF'
hello
$PWD
EOF
where that bit before the first EOF is exactly the same as the before the second (tab, four spaces, two tabs, whatever). This allows you to keep your nice indenting, although it doesn't expand variables inside the here-document ($PWD doesn't change).
3/ Allow tabs to be stripped from the start of input lines and the end marker.
cat <<-EOF
hello
$PWD
EOF
but there's no way to get tabs into the beginnings of lines.
4/ For your purposes, you can also use:
( echo "$DB_ADMIN";
echo "" ;
echo "0" ;
echo "y" ;
echo "n"
) | db.sh
check_status
sqlplus $DB_SCHEMA#$DB_NAME < initial_data.sql
cd -
I believe number 4 is the best option for you. It allows nice lining up of the input, tabs and spaces anywhere in the lines and variable expansion.
The end of your "Here document" needs to be unindented, I'm afraid.
The ENDC label must be alone in a line without leading/trailing whitspaces.

Resources