Many times I have copy and pasted from a guide that had the following instruction:
$ echo "Execute xyz"
and would get the error
$: command not found
So I created a bash script to ignore the '$' at the beginning; The solution is as follows:
- Put the below script in a file called '$'
vim $
- Make it executable
chmod +x $
- Add it to your bin
sudo cp $ /usr/bin/$
The script:
#!/bin/bash
eval $@
If you use zsh, you can put the following in your .zshrc:
alias "$"=""
4 Answers 4
While I understand why you're trying to do what you do, but honestly, I'd really avoid masking something as critical as a sigil. Before you know it, you'll end up doing something similar for #
(as some commands out there assume you're running them as root). That's just going to be a mess.
What you could do instead, is write a script that processes (and if needed) trims whatever is in your clipboard. I'm going to assume you're running some flavour of Linux for the purposes of simplicity. There are similar utilities for OSX, so if needed you can just modify the approach suggested here to fit your system.
Simple and risky
Apart from what you already have, install xclip
, a command that allows you to interact with your system clipboard from the command line (e.g. xclip -selection c file.txt
saves contents of file.txt to clipboard).
Now xclip
can also print out (and as such access) whatever is in your clipboard by running xclip -o
or xclip -out
, so let's start with that:
#!/usr/bin/env bash
str="$(xclip -o)"
Great, so now str
contains whatever is in your system clipboard. Now if you've accidentally selected the leading $
, chances are you've accidentally selected the space before it, or maybe even a line-break. To be safe, it's probably best we trim the string in question. There's ways to do this with bash's many weird and wacky string manipulation constructs, but keeping it simple, we can just use xargs
which, if no other arguments are provided, will trim the input string and return it as-is:
str="$(xclip -o | xargs)"
OK, so now we can check to see if the string in question starts with a $
:
if [[ "${str:0:1}" == '$' ]]; then
str="${str:1}"
fi
So if the first character of our string is $
, we're cutting it off, and setting str
to be equal to str
minus the first character, getting rid of the dollar sign. You could add something like:
if [[ "${str:0:1}" == '$' ]] || [[ "${str:0:1}" == '#' ]] ; then
str="${str:1}"
fi
To account for the aforementioned leading #
characters you may have accidentally copied. At this point, you're ready to do what you're currently doing:
## Either using eval:
eval $str
## or the shorter, but equally risky
$str
Make the script executable and call it something short and to the point:
$ vim ~/bin/execpaste
#!/usr/bin/env bash
str="$(xclip -o | xargs)"
if [[ "${str:0:1}" == '$' ]] || [[ "${str:0:1}" == '#' ]]; then
str="${str:1}"
fi
echo "Executing ${str}..."
$str
Make the script executable ($ chmod +x ~/bin/execpaste
), and add ~/bin
to your path (rather than adding scripts directly to /usr/bin
) by adding this to your .bashrc
or .profile
file:
export PATH="${PATH}:${HOME}/bin"
Slightly less risky
Personally, though, I prefer to be able to see what I'm pasting in my terminal before executing it. Just to be sure... In that case, I'd keep the first part of the script, but call it prepclip
or something (short for prepare clipboard). Most of it would remain the same, apart from the last part (where the command is actually executed). I'd just write it back to my clipboard instead:
#!/usr/bin/env bash
str="$(xclip -o | xargs)"
if [[ "${str:0:1}" == '$' ]] || [[ "${str:0:1}" == '#' ]]; then
str="${str:1}"
fi
# send updated string to clipboard
echo "${str}" | xclip -selection c
In terms of actually using this script, it does indeed take 2 steps:
$ prepclip
$ <paste>
But the second step, to me, is a worthwhile sanity check.
MacOS
MacOS, as far as I know has 2 commands to paste and copy that you can use instead. Below is the MacOS version of the last script (cleaning up clipboard input and writes in back to the clipboard), just in case you're on MacOS:
#!/usr/bin/env bash
str="$(pbpaste | xargs)"
if [[ "${str:0:1}" == '$' ]] || [[ "${str:0:1}" == '#' ]]; then
str="${str:1}"
fi
echo "${str}" | pbcopy
That should give you everything you need, without messing with sigils.
-
\$\begingroup\$ That's a little inconvenient, because then you have to remember to pass everything through the
CLIPBOARD
selection instead of the more naturalPRIMARY
one. \$\endgroup\$Toby Speight– Toby Speight2021年03月25日 18:02:39 +00:00Commented Mar 25, 2021 at 18:02 -
\$\begingroup\$ If you're using Bash, then simply substituting
str=${str##[#$] }
should be simpler than the substring stuff. You likely want to apply it to each line; I'd probably just pay the cost of passing it throughsed
then. \$\endgroup\$Toby Speight– Toby Speight2021年03月25日 18:07:02 +00:00Commented Mar 25, 2021 at 18:07 -
\$\begingroup\$ I have found this stackoverflow.com/questions/51528677/… \$\endgroup\$Alex Angel– Alex Angel2021年03月25日 19:13:53 +00:00Commented Mar 25, 2021 at 19:13
Change the script to simply "$@"
and it should work for commands other than shell builtins. Showing a session with PS1='\$ '
:
$ cat '/usr/bin/$'
#!/usr/bin/env bash
"$@"
$ $ echo 'a b'
a b
$ $ [[ a = b ]]
/usr/bin/$: line 2: [[: command not found
Caveat: While this is a neat trick it encourages dangerous behaviour. Copying and pasting from sources which are not completely trusted is a big security risk (1, 2).
-
\$\begingroup\$ Some other commands from my reply still wouldn't work. \$\endgroup\$choroba– choroba2021年03月25日 18:29:02 +00:00Commented Mar 25, 2021 at 18:29
-
\$\begingroup\$ It won't work on builtins, no. But it's as close as you're ever going to get. \$\endgroup\$l0b0– l0b02021年03月25日 18:33:20 +00:00Commented Mar 25, 2021 at 18:33
Cute idea. It's not so simple, though.
For example, compare the outputs of
echo 'a b'
with
$ echo 'a b'
(because of word splitting).
Also, it fails for compound commands like
for i in a b c ; do echo $i ; done
(because everything after the first ;
is another command).
Some commands might fail depending on the contents of the current directory, e.g.
$ [[ a = ? ]]
(if it works, try running touch 1 2 3
).
-
\$\begingroup\$ Oh I hadn't thought of that \$\endgroup\$Alex Angel– Alex Angel2021年03月24日 23:08:24 +00:00Commented Mar 24, 2021 at 23:08
-
\$\begingroup\$ Updated with some more problematic cases. \$\endgroup\$choroba– choroba2021年03月24日 23:12:30 +00:00Commented Mar 24, 2021 at 23:12
-
\$\begingroup\$ To solve the first case, we can do Line 1:
#!/bin/bash
Line 2:"$@"
\$\endgroup\$Alex Angel– Alex Angel2021年03月24日 23:18:54 +00:00Commented Mar 24, 2021 at 23:18
There's a straightforward flaw that needs fixing - outside of quotes, $@
behaves like $*
, so you've lost the word boundaries. What you really wanted was
#!/bin/sh
exec "$@"
Note that plain shell is appropriate here, as the invoking shell has already done all the shelly things with the arguments, such as word splitting and the various expansions. You certainly didn't want eval
!
The other case where this program doesn't achieve the advertised results are when the pasted commands are intended to change the state of the shell - for example by setting environment variables. With the code here, those modifications happen in the process running the $
program, not the calling shell.
What would probably work better is a shell function called $
.
-
\$\begingroup\$ This still fails with some of the examples in my reply. \$\endgroup\$choroba– choroba2021年03月25日 18:31:02 +00:00Commented Mar 25, 2021 at 18:31
-
\$\begingroup\$ You can't create a Bash function or alias with the name
$
. \$\endgroup\$l0b0– l0b02021年03月25日 18:32:54 +00:00Commented Mar 25, 2021 at 18:32 -
1\$\begingroup\$ It looks like you can do
alias "$"=""
in zsh (tested, I was using the gnome shell at the time of writing) \$\endgroup\$Alex Angel– Alex Angel2021年03月25日 19:16:53 +00:00Commented Mar 25, 2021 at 19:16 -
\$\begingroup\$ @choroba, yes absolutely. There's no way in shell that I know of that even the function approach would work with flow-control commands. I tried to give useful feedback that could improve other shell scripts, even though this one is doomed (or at least severely limited). \$\endgroup\$Toby Speight– Toby Speight2021年03月25日 21:00:06 +00:00Commented Mar 25, 2021 at 21:00
-
\$\begingroup\$ @l0b0, I thought that might be impossible with unmodified Bash, but wasn't sufficiently certain I could prove it, given ways to sneak functions in through the environment for example. Of course, if it matters enough to someone, it wouldn't be too hard to modify a shell to accept
$
as a function identifier. I still wouldn't recommend it! \$\endgroup\$Toby Speight– Toby Speight2021年03月25日 21:02:39 +00:00Commented Mar 25, 2021 at 21:02