1

I am trying to copy the & sign to the clipboard using the code below. But it just gives me an error "| was unexpected at this time."

import os

def addToClipBoard(text):
    command = 'echo ' + text.strip() + '| clip'
    os.system(command)

addToClipBoard('&') 

Is there a way to make it work with a little tweaking, or do I have to use another way?

TickyPlays
  • 27
  • 3
  • 1
    Which operating system? Is `clip` an executable program? Then you could use the subprocess module and pipe the value to its stdin. This would avoid shell escapes in os.system. There is also the `clipboard` module installable via pip. – tdelaney Jul 02 '22 at 15:43
  • 1
    Using `os.system()` for this exposes you to serious security risks. You should use `subprocess.Popen` **without** `shell=True`. – Charles Duffy Jul 02 '22 at 15:43

3 Answers3

2

You need to quote (or escape) the & character:

import os

def addToClipBoard(text):
    command = "echo '{}' | clip".format(text.strip())
    os.system(command)

addToClipBoard('&') 

In Bash, & is a control operator. It’s a shell builtin, meaning, it’s literally a core part of the Bash tool set. (Copied from BashItOut - Ampersands & on the command line)


EDIT

As @CharlesDuffy mentioned in the comments, there's a better way of doing this for security reasons.

Consider the string $(rm -rf ~/*)'$(rm -rf ~/*)' -- trying to copy it to the clipboard is going to result in a deleted home directory even with the extra quotes, because the quotes inside the string itself cancel them out.

The solution is to use shlex.quote() as a safer means of adding literal quotes.

import os
import shlex

def addToClipBoard(text):
    command = "printf '%s\n' {} | clip".format(shlex.quote(text))
    os.system(command)

addToClipBoard('&') 

NOTE: This last section is based on the comments by @CharlesDuffy.

Beni Trainor
  • 346
  • 1
  • 11
  • 1
    Consider the string `$(rm -rf ~/*)'$(rm -rf ~/*)'` -- trying to copy it to the clipboard is going to result in a deleted home directory even with the extra quotes, because the quotes inside the string itself cancel them out. – Charles Duffy Jul 02 '22 at 15:44
  • You're right. I was just considering this single case. – Beni Trainor Jul 02 '22 at 15:45
  • 1
    If you don't want to delete the answer, consider using `shlex.quote()` as a safer means of adding literal quotes. (Note that you should be using it _instead of_, not _in addition to_, the extra `'`s). Also think of switching from `echo` to `printf '%s\n'` for the reasons described in [Why is printf better than echo?](https://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo) – Charles Duffy Jul 02 '22 at 15:46
  • 2
    @CharlesDuffy is completely right about safety. Nice comment to mention, But OP asked about **why** my code doesn't work and this answer exactly pointed that out. That's why I upvoted this. – S.B Jul 02 '22 at 15:49
  • 1
    @S.B, if the answer kept itself to _only_ answering that I'd have no problem with it; but it also suggests a solution with blatant security problems (that show up very regularly in the real world -- I've seen lots of embedded products with admin UIs with this kind of vulnerability, f/e). I don't consider the demonstration of dangerous practices in a teaching context forgivable, even when accompanied by correct information. – Charles Duffy Jul 02 '22 at 15:51
  • 1
    Well, not forgivable until it's edited to correct the bug, which should be easy enough to do. :) – Charles Duffy Jul 02 '22 at 15:51
  • @CharlesDuffy I've added your suggestions. – Beni Trainor Jul 02 '22 at 15:54
2

you running shell command to copy text to clip board but '&' sign in shell command is used to run command in background so maybe that's why it's not working.

you can directly copy text to clip board using python like this :

import pyperclip
pyperclip.copy('The text to be copied to the clipboard.')

code from here : Python script to copy text to clipboard

mohan
  • 54
  • 1
  • 4
  • This is the only answer that has the decency to realize escaping strings is too much of a hassle not to let a library do it for you. – jthulhu Jul 02 '22 at 15:50
  • @BlackBeans Let a library do it for you, _or structure things so it doesn't have to be done at all_. That's the case with the answer by Samwise (which predated this one) -- there's no shell, so there's no need for any kind of shell escaping in the first place. – Charles Duffy Jul 02 '22 at 16:19
1

os.system doesn't itself know that the | character is supposed to redirect stdin, and depending on the OS it's not necessarily running your command through a shell that would do that. If you use subprocess instead you can pass data to stdin directly, e.g.:

import subprocess

def add_to_clipboard(text: str) -> None:
    subprocess.run("clip", text=True, input=text.strip())
Samwise
  • 68,105
  • 3
  • 30
  • 44
  • This is the only safe/secure answer yet given. – Charles Duffy Jul 02 '22 at 15:45
  • (that comment was added before I saw the "pyperclip" answer, which may well be safe -- I'd need to read the library's source code to say). – Charles Duffy Jul 02 '22 at 15:47
  • 1
    ...btw, I suspect that it might be necessary for you to discuss the syntactic meaning of the `&` character, not just the `|` character, for some of the folks who are sticklers for the "was the literal 'why' question answered?" side of the question to consider this answer correct. Particularly, talking about why `foo & | bar` is invalid syntax would be covering ground that right now no answer touches on. – Charles Duffy Jul 02 '22 at 15:53