Bash: How do I create a variable containing a list of arguments with multi-word tokens? - bash

I want to create a variable which is essentially a list of arguments to pass to a command or function. The pattern below with arg_string works well for foo, bar, and baz, but not for "multi word token", which I would like the command to see as a single argument.
#!/bin/bash
function func() {
for arg in "$#"
do
echo ${arg}
done
}
arg_string="foo bar baz \"multi word token\""
arg_string="foo bar baz multi\ word\ token"
arg_string="foo bar baz 'multi word token'"
func ${arg_string}
Here is the output:
foo
bar
baz
'multi
word
token'
When I want:
foo
bar
baz
multi word token

Just stick an eval before the function call:
eval func ${arg_string}

Related

Bash: SPACE-triggered completion

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

Display Unique Shell Columns

Given we have two formatted strings that are unrelated to each other.
#test.rb
string_1 = "Title\nfoo bar\nbaz\nfoo bar baz boo"
string_2 = "Unrelated Title\ndog cat farm\nspace moon"
How can I use ruby or call shell commands to have each of these string display as columns in terminal? The key is that the data of each string are not building a correlated row, ie this is not a table, rather 2 lists side by side.
Title Unrelated Title
foo bar dog cat farm
baz space moon
foo bar baz boo
You can try using paste and column command together. Note that this is a shell command so spaces between the assignment operator should be corrected.
$ string_1="Title\nfoo bar\nbaz\nfoo bar baz boo"
$ string_2="Unrelated Title\ndog cat farm\nspace moon"
$ paste -d '|' <(echo -e "$string_1") <(echo -e "$string_2") | column -s'|' -t
Title Unrelated Title
foo bar dog cat farm
baz space moon
foo bar baz boo
We paste the lines with | as delimiter and tell column command to use | as a reference to form columns.
In Ruby, you could do it this way:
#!/usr/bin/env ruby
string_1 = "Title\nfoo bar\nbaz\nfoo bar baz boo"
string_2 = "Unrelated Title\ndog cat farm\nspace moon"
a1 = string_1.split("\n")
a2 = string_2.split("\n")
a1.zip(a2).each { |pair| puts "%-20s%s" % [pair.first, pair.last] }
# or
# a1.zip(a2).each { |left, right| puts "%-20s%s" % [left, right] }
This produces:
Title Unrelated Title
foo bar dog cat farm
baz space moon
foo bar baz boo
Hi , If you Use temp files
string_1 = "Title\nfoo bar\nbaz\nfoo bar baz boo"
string_2 = "Unrelated Title\ndog cat farm\nspace moon"
echo -e $string_1 >a.txt
echo -e $string_2 >b.txt
paste a.txt b.txt
I hope it will help.

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

Ruby regex- matching text if it doesn't start with a character

I was wondering how you are able to match a pattern only if it doesn't start with a specific character. I would like to match "foo" but I would not like to match "afoo." What kind of regex operator do I need for that? I can't seem to find the right one. Maybe an anchor?
For example I'd like to change
foo foo afoo foo
to
bar bar afoo bar
Thanks.
Although the answer below is correct for my example, what about if it was /foo instead of afoo? That doesn't seem to behave the same?
Sounds like you're looking for a negative look behind. If you say (?<!expr1)expr2 then it will match whatever expr2 matches as long as it isn't immediately preceded by something expr1 matches. For example:
>> 'foo foo afoo foo'.gsub(/(?<!a)foo/, 'bar')
=> "bar bar afoo bar"
str = "foo foo afoo foo"
str.gsub(/\bfoo/, "bar") #=> "bar bar afoo bar"

Redmine plugin that replaces words via regular expression?

I'm very new to Redmine/Ruby trying to achieve a simple plugin that takes the current wiki page content and matches/replaces everytime a word occurs via regular expression. How can I do this?
Thanks!
Dennis
The word replacement can de done by using gsub() with \b to match a word boundary:
irb(main):001:0> 'foo bar baz foo bar'.gsub /\bfoo\b/, 'replaced'
=> "replaced bar baz replaced bar"
Here is a more complete solution with a dictionary of words to replace:
repl = {'foo'=>'apple', 'baz'=>'banana'}
s = 'foo bar baz foo bar'
for from, to in repl:
s = s.gsub /\b#{from}\b/, to
end
Result: apple bar banana apple bar

Resources