0
\$\begingroup\$

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:

  1. Put the below script in a file called '$'
    • vim $
  2. Make it executable
    • chmod +x $
  3. Add it to your bin
    • sudo cp $ /usr/bin/$

The script:

#!/bin/bash
eval $@
\$\endgroup\$
1
\$\begingroup\$

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).

\$\endgroup\$
3
  • \$\begingroup\$ Oh I hadn't thought of that \$\endgroup\$ – Alex Angel Mar 24 at 23:08
  • \$\begingroup\$ Updated with some more problematic cases. \$\endgroup\$ – choroba Mar 24 at 23:12
  • \$\begingroup\$ To solve the first case, we can do Line 1: #!/bin/bash Line 2: "$@" \$\endgroup\$ – Alex Angel Mar 24 at 23:18
1
\$\begingroup\$

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).

\$\endgroup\$
2
  • \$\begingroup\$ Some other commands from my reply still wouldn't work. \$\endgroup\$ – choroba Mar 25 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 Mar 25 at 18:33
0
\$\begingroup\$

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.

\$\endgroup\$
3
  • \$\begingroup\$ That's a little inconvenient, because then you have to remember to pass everything through the CLIPBOARD selection instead of the more natural PRIMARY one. \$\endgroup\$ – Toby Speight Mar 25 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 through sed then. \$\endgroup\$ – Toby Speight Mar 25 at 18:07
  • \$\begingroup\$ I have found this stackoverflow.com/questions/51528677/… \$\endgroup\$ – Alex Angel Mar 25 at 19:13
0
\$\begingroup\$

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 $.

\$\endgroup\$
6
  • \$\begingroup\$ This still fails with some of the examples in my reply. \$\endgroup\$ – choroba Mar 25 at 18:31
  • \$\begingroup\$ You can't create a Bash function or alias with the name $. \$\endgroup\$ – l0b0 Mar 25 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 Mar 25 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 Mar 25 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 Mar 25 at 21:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.