Ignore "$" at the beginning of the command in Bash - bash

On many websites, "$" is written at the beginning when introducing the Linux command.
But of course, this will result in a "$: command not found" error.
To avoid this it is necessary to delete or replace "$" every time, but it is troublesome.
So, if the beginning of the input command is "$", I think that it would be good if I could ignore "$", is it possible?

If you really need this, you can create a file in a directory that is in your $PATH. The file will be named $ and will contain
#!/bin/bash
exec "$#"
Make it executable, then you can do
$ echo foo bar
foo bar
$ $ echo foo bar
foo bar
$ $ $ echo foo bar
foo bar
$ $ $ $ echo foo bar
foo bar
Note that this does not affect variable expansion in any way. It only interprets a standalone $ as the first word in the command line as a valid command.
I just noticed a problem with this: It works for calling commands, but not for shell-specific constructs:
$ foo=bar
$ echo $foo
bar
$ $ foo=qux
/home/jackman/bin/$: line 2: exec: foo=qux: not found
and
$ { echo hello; }
hello
$ $ { echo hello; }
bash: syntax error near unexpected token `}'
In summary, everyone else is right: use your mouse better.

Yes it is possible for you to ignore the command prompt, when copying commands from web sites. Use the shift and arrow keys to ignore the prompt. This will also help you to ignore the use of the # sign, which is used to indicate commands, which need administrative privileges.

Related

Why do alias foo1="echo $1" and alias foo2="echo '$1'" behave as they do?

In my .bashrc file, I put the following two lines:
alias foo1="echo $1"
alias foo2="echo '$1'"
Then, in the terminal, I get the following output:
$ foo1 hello world
hello world
$ foo2 hello world
hello world
Why is the extra space produced by foo2?
If I just do the following in terminal, the outputs are as shown:
$ echo hello world
hello world
$ echo 'hello world'
hello world
This leads me to think that foo1 and foo2 should do exactly the same thing. How come they do not actually output exactly the same thing, and why do they differ by simply one space character?
(Also, why do either of them output world? I would expect only the first argument to be outputted.)
If, in a typical interactive shell, you run:
$ set -- # this clears the argument list; it's empty by default, but make sure.
$ alias foo1="echo $1"
$ alias foo2="echo '$1'"
$ alias -p
The output of alias -p is as follows:
$ alias -p
alias foo1='echo '
alias foo2='echo '\'''\'''
Notably, the $1s are not present at all -- because you defined the alias in a double-quoted string, they were replaced with the current value of $1 -- the value present in the current shell's context, which for an interactive shell is going to be empty at startup.
Thus, when you run:
foo2 hello world
...what the shell invokes is:
echo '' hello world
Because echo puts spaces between its arguments, this prints out as:
hello world
Whereas, if you run:
foo1 hello world
...what the shell invokes is:
echo hello world
...because, as you can see in the foo1 alias emitted by alias -p, there is no record of the unquoted $1 left; it was replaced with its current value -- an empty string -- at definition time.
aliases do not take positional arguments. $1 will evaluate to nothing inside an alias. foo2 is literally appending what you typed after foo2 to the empty string '', which includes the space after foo2. foo1 is not appending to anything as $1 evaluates to nothing, so the space is not shown.

How to insert command line arguments into bind command?

Please consider the following file inject.sh with the following line:
#!/bin/bash
bind '"\e[0n": "echo test"'; printf '\e[5n'
When running source inject.sh it injects the text 'echo test' on a new line (not echo). This works correctly, as per one of the suggestions here: https://unix.stackexchange.com/a/213821
I want to replace the "echo test" part with all the command line arguments that might be provided to the script, so with $# basically. However I am having a hard time adding it into the command. I have tried with:
#!/bin/bash
bind '"\e[0n": "'$#'"'; printf '\e[5n'
But it only works if only one argument is passed to the command. So for example:
source inject.sh ls --> bash-3.2$ ls| OK (| is the cursor)
source inject.sh echo foo --> bash-3.2$ echo| NOT OK (does not print 'foo' and additionally it messes up the terminal, can't print some letters anymore)
Not sure where the problem is... Maybe wrong string concatenation?
Note this is a bash specific problem, not zsh, fish or something else. But for reference I am trying to emulate the zsh behavior of print -z $# echo foo
You can use:
#!/bin/bash
bind '"\e[0n": "'"$*"'"'; printf '\e[5n'
When you call source inject.sh foo bar baz, you want to concatenate them and put them in the same argument:
bind '"\e[0n": "foo bar baz"'
But you instead, you were splitting it across three invalid arguments:
bind '"\e[0n": "foo' 'bar' 'baz"'
Ways to debug this includes ShellCheck, which spots both problems:
In inject.sh line 2:
bind '"\e[0n": "'$#'"'; printf '\e[5n'
^-- SC2068: Double quote array expansions to avoid re-splitting elements.
^-- SC2145: Argument mixes string and array. Use * or separate argument.
And set -x which shows how the command is being mangled:
++ bind '"\e[0n": "foo' bar 'baz"' # Invalid attempt
++ bind '"\e[0n": "foo bar baz"' # Valid command

Bash alias using !$

I found out today that I can write !$ to get the last argument from the last command executed.
Now I'm trying to create an alias using that shortcut and it isn't working at all.
These are the ones I'm trying to create.
alias gal='git add !$'
alias gcl='git checkout !$'
alias sl='sublime !$'
And this is the result output when calling gal or gcl
fatal: pathspec '!$' did not match any files
So it seems like !$ just isn't being replaced by the last argument from the last command in this context.
Is it possible?
Instead of fiddling with Bash's history, you might as well want to use Bash's $_ internal variable: The relevant part of the manual states:
$_: […] expands to the last argument to the previous command, after expansion. […]
For example:
$ touch one two three
$ echo "$_"
three
$ ls
$ echo "$_"
ls
$ a='hello world'
$ echo $a
hello world
$ echo "$_"
world
$ echo "$a"
hello world
$ echo "$_"
hello world
$
In your case, your aliases would look like:
alias gal='git add "$_"'
alias gcl='git checkout "$_"'
alias sl='sublime "$_"'
You can use the bash builtin history command fc: an example
$ alias re_echo='echo $(fc -ln -2 | awk '\''NR==1 {print $NF}'\'')'
$ echo foo
foo
$ re_echo bar
foo bar
$ re_echo baz
bar baz
$ re_echo qux
baz qux

How do I set variables from verbatim shell output?

I thought this should be simple, but now that I'm trying it I can't figure it out. Did I take stupid pills this morning?
I have a command with output that is some variables that I want set. I thought I could use eval, but that apparently doesn't work.
Here's what I want to do:
$ ./foo
FOO=bar
BAR=baz
$ eval ./foo
$ echo $FOO
bar
How do I set those directly?
You need to evaluate the output of the script, not the string ./foo
$ eval $( ./foo )
What you try todo is sourcing the foo file into your shell. You can do that by invoking dot command and give your file as argument (the file does not have to be executable)
$ . ./foo
$ echo $FOO
bar

bash: treat quotes in quotes

I isolated a problem in my script to this small example. That's what I get:
$ cmd="test \"foo bar baz\""
$ for i in $cmd; do echo $i; done
test
"foo
bar
baz"
And that's what I expected:
$ cmd="test \"foo bar baz\""
$ for i in $cmd; do echo $i; done
test
"foo bar baz"
How can I change my code to get the expected result?
UPDATE Maybe my first example was not good enough. I looked at the answer of Rob Davis, but I couldn't apply the solution to my script. I tried to simplify my script to describe my problem better. This is the script:
#!/bin/bash
function foo {
echo $1
echo $2
}
bar="b c"
baz="a \"$bar\""
foo $baz
This it the expected output compared to the output of the script:
expected script
a a
"b c" "b
First, you're asking the double-quotes around foo bar baz to do two things simultaneously, and they can't. You want them to group the three words together, and you want them to appear as literals. So you'll need to introduce another pair.
Second, parsing happens when you set cmd, and cmd is set to a single string. You want to work with it as individual elements, so one solution is to use an array variable. sh has an array called #, but since you're using bash you can just set your cmd variable to be an array.
Also, to preserve spacing within an element, it's a good idea to put double quotes around $i. You'd see why if you put more than one space between foo and bar.
$ cmd=(test "\"foo bar baz\"")
$ for i in "${cmd[#]}"; do echo "$i"; done
See this question for more details on the special "$#" or "${cmd[#]}" parsing feature of sh and bash, respectively.
Update
Applying this idea to the update in your question, try setting baz and calling foo like this:
$ baz=(a "\"$bar\"")
$ foo "${baz[#]}"
Why quote it in the first place?
for i in test "foo bar baz"; do echo $i; done

Resources