Copying the contents of a variable to the clipboard - python

I am trying to copy the contents of a variable to the clipboard automatically within a python script. So, a variable is created that holds a string, and I'd like to copy that string to the clipboard.
Is there a way to do this with Pyclips or
os.system("echo '' | pbcopy")
I've tried passing the variable where the string should go, but that doesn't work which makes sense to me.

Have you tried this?
import os
def addToClipBoard(text):
command = 'echo ' + text.strip() + '| clip'
os.system(command)
Read more solutions here.
Edit:
You may call it as:
addToClipBoard(your_variable)

Since you mentioned PyCLIPS, it sounds like 3rd-party packages are on the table. Let me thrown a recommendation for pyperclip. Full documentation can be found on GitHub, but here's an example:
import pyperclip
variable = 'Some really "complex" string with\na bunch of stuff in it.'
pyperclip.copy(variable)
While the os.system(...'| pbcopy') examples are also good, they could give you trouble with complex strings and pyperclip provides the same API cross-platform.

The accepted answer was not working for me as the output had quotes, apostrophes and $$ signs which were interpreted and replaced by the shell.
I've improved the function based on answer. This solution uses temporary file instead of echoing the string in the shell.
def add_to_clipboard(text):
import tempfile
with tempfile.NamedTemporaryFile("w") as fp:
fp.write(text)
fp.flush()
command = "pbcopy < {}".format(fp.name)
os.system(command)
Replace the pbcopy with clip for Windows or xclip for Linux.

For X11 (Unix/Linux):
os.system('echo "%s" | xsel -i' % variable)
xsel also gives you a choice of writing to:
the primary selection (default)
the secondary selection (-s option), or
the clipboard (-b option).
If xsel doesn't work as you expect, it is probably because you are using the wrong selection/clipboard.
In addition, with the -a option, you can append to the clipboard instead of overwrite. With -c, the clipboard is cleared.
Improvement
The module subprocess provides a more secure way to do the same thing:
from subprocess import Popen, PIPE
Popen(('xsel', '-i'), stdin=PIPE).communicate(variable)

Related

python remove command subprocess.call with regex

I am trying to execute rm command from python in linux as follows
remove_command = [find_executable(
"rm"), "-rf", "dist/", "python_skelton.egg-info", "build/", "other/*_generated.py"]
print('Removing build, dist, python_skelton.egg-
if subprocess.call(remove_command) != 0:
sys.exit(-1)
The directories gets removed successfully but the regex pattern other/*_generated.py
does not remove the relevant _generated.py files.
How shall I remove those files using regex from python script?
The reason this doesn't work the way you intend it to, is that your pattern is not expanded, but interpreted as the litteral file name "other/*_generated.py". This happens because you are relying on so-called glob pattern expansion.
The glob pattern is typically expanded by the shell, but since you are calling the rm command without using the shell, you will not get this "automatically" done. I can see two obvious ways to handle this.
Expand the glob before calling the subprocess
This can be done, using the Python standard library glob implementation:
import glob
remove_command = [find_executable("rm"), "-rf", "dist/", "python_skelton.egg-info",
"build/"] + glob.glob("other/*_generated.py")
subprocess.call(remove_command)
Use the shell to expand the glob
To do this, you need to pass shell=True to the subprocess.call. And, as always, when using the shell, we should pass the command as a single string and not a list:
remove_command = [find_executable("rm"), "-rf", "dist/", "python_skelton.egg-info",
"build/", "other/*_generated.py"]
remove_command_string = " ".join(remove_command) # generate a string from list
subprocess.call(remove_command_string, shell=True)
Both of these approaches will work. Note that if you allow user input, you should avoid using shell=True though, as it is a security hole, that can be used to execute arbitrary commands. But, in the current use case, it seems to not be the case.

I am trying to print the last line of every file in a directory using shell command from python script

I am storing the number of files in a directory in a variable and storing their names in an array. I'm unable to store file names in the array.
Here is the piece of code I have written.
import os
temp = os.system('ls -l /home/demo/ | wc -l')
no_of_files = temp - 1
command = "ls -l /home/demo/ | awk 'NR>1 {print $9}'"
file_list=[os.system(command)]
for i in range(len(file_list))
os.system('tail -1 file_list[i]')
Your shell scripting is orders of magnitude too complex.
output = subprocess.check_output('tail -qn1 *', shell=True)
or if you really prefer,
os.system('tail -qn1 *')
which however does not capture the output in a Python variable.
If you have a recent-enough Python, you'll want to use subprocess.run() instead. You can also easily let Python do the enumeration of the files to avoid the pesky shell=True:
output = subprocess.check_output(['tail', '-qn1'] + os.listdir('.'))
As noted above, if you genuinely just want the output to be printed to the screen and not be available to Python, you can of course use os.system() instead, though subprocess is recommended even in the os.system() documentation because it is much more versatile and more efficient to boot (if used correctly). If you really insist on running one tail process per file (perhaps because your tail doesn't support the -q option?) you can do that too, of course:
for filename in os.listdir('.'):
os.system("tail -n 1 '%s'" % filename)
This will still work incorrectly if you have a file name which contains a single quote. There are workarounds, but avoiding a shell is vastly preferred (so back to subprocess without shell=True and the problem of correctly coping with escaping shell metacharacters disappears because there is no shell to escape metacharacters from).
for filename in os.listdir('.'):
print(subprocess.check_output(['tail', '-n1', filename]))
Finally, tail doesn't particularly do anything which cannot easily be done by Python itself.
for filename in os.listdir('.'):
with open (filename, 'r') as handle:
for line in handle:
pass
# print the last one only
print(line.rstrip('\r\n'))
If you have knowledge of the expected line lengths and the files are big, maybe seek to somewhere near the end of the file, though obviously you need to know how far from the end to seek in order to be able to read all of the last line in each of the files.
os.system returns the exitcode of the command and not the output. Try using subprocess.check_output with shell=True
Example:
>>> a = subprocess.check_output("ls -l /home/demo/ | awk 'NR>1 {print $9}'", shell=True)
>>> a.decode("utf-8").split("\n")
Edit (as suggested by #tripleee) you probably don't want to do this as it will get crazy. Python has great functions for things like this. For example:
>>> import glob
>>> names = glob.glob("/home/demo/*")
will directly give you a list of files and folders inside that folder. Once you have this, you can just do len(names) to get the first command.
Another option is:
>>> import os
>>> os.listdir("/home/demo")
Here, glob will give you the whole filepath /home/demo/file.txt and os.listdir will just give you the filename file.txt
The ls -l /home/demo/ | wc -l command is also not the correct value as ls -l will show you "total X" on top mentioning how many total files it found and other info.
You could likely use a loop without much issue:
files = [f for f in os.listdir('.') if os.path.isfile(f)]
for f in files:
with open(f, 'rb') as fh:
last = fh.readlines()[-1].decode()
print('file: {0}\n{1}\n'.format(f, last))
fh.close()
Output:
file.txt
Hello, World!
...
If your files are large then readlines() probably isn't the best option. Maybe go with tail instead:
for f in files:
print('file: {0}'.format(f))
subprocess.check_call(['tail', '-n', '1', f])
print('\n')
The decode is optional, although for text "utf-8" usually works or if it's a combination of binary/text/etc then maybe something such as "iso-8859-1" usually should work.
you are not able to store file names because os.system does not return output as you expect it to be. For more information see : this.
From the docs
On Unix, the return value is the exit status of the process encoded in the format specified for wait(). Note that POSIX does not specify the meaning of the return value of the C system() function, so the return value of the Python function is system-dependent.
On Windows, the return value is that returned by the system shell after running command, given by the Windows environment variable COMSPEC: on command.com systems (Windows 95, 98 and ME) this is always 0; on cmd.exe systems (Windows NT, 2000 and XP) this is the exit status of the command run; on systems using a non-native shell, consult your shell documentation.
os.system executes linux shell commands as it is. for getting output for these shell commands you have to use python subprocess
Note : In your case you can get file names using either glob module or os.listdir(): see How to list all files of a directory

Python plumbum: Passing $ in an cmd argument

I am trying to execute the following command in python using plumbum:
sort -u -f -t$'\t' -k1,1 file1 > file2
However, I am having issues passing the -t$'\t' argument. Here is my code:
from plumbum.cmd import sort
separator = r"-t$'\t'"
print separator
cmd = (sort["-u", "-f", separator, "-k1,1", "file1"]) > "file2"
print cmd
print cmd()
I can see problems right away after print separator and print cmd() executes:
-t$'\t'
/usr/bin/sort -u -f "-t\$'\\t'" -k1,1 file1 > file2
The argument is wrapped in double quotes.
An extra \ before $ and \t is inserted.
How should I pass this argument to plumbum?
You may have stumbled into limitations of the command line escaping.
I could make it work using subprocess module, passing a real tabulation char litteraly:
import subprocess
p=subprocess.Popen(["sort","-u","-f","-t\t","-k1,1","file1",">","file2"],shell=True)
p.wait()
Also, full python short solution that does what you want:
with open("file1") as fr, open("file2","w") as fw:
fw.writelines(sorted(set(fr),key=lambda x : x.split("\t")[0]))
The full python solution doesn't work exactly the same way sort does when dealing with unicity. If 2 lines have the same first field but not the same second field, sort keeps one of them, whereas the set will keep both.
EDIT: unchecked but you just confirmed that it works: just tweak your plumbum code with:
separator = "-t\t"
could just work, although out of the 3 ones, I'd recommend the full python solution since it doesn't involve an external process and therefore is more pythonic and portable.

Passing shell commands with Python os.system() or subprocess.check_call()

I'm trying to call 'sed' from Python and having troubles passing the command line via either subprocess.check_call() or os.system().
I'm on Windows 7, but using the 'sed' from Cygwin (it's in the path).
If I do this from the Cygwin shell, it works fine:
$ sed 's/&nbsp;/\ /g' <"C:foobar" >"C:foobar.temp"
In Python, I've got the full pathname I'm working with in "name". I tried:
command = r"sed 's/&nbsp;/\ /g' " + "<" '\"' + name + '\" >' '\"' + name + '.temp' + '\"'
subprocess.check_call(command, shell=True)
All the concatenation is there to make sure I have double quotes around the input and output filenames (in case there are blank spaces in the Windows file path).
I also tried it replacing the last line with:
os.system(command)
Either way, I get this error:
sed: -e expression #1, char 2: unterminated `s' command
'amp' is not recognized as an internal or external command,
operable program or batch file.
'nbsp' is not recognized as an internal or external command,
operable program or batch file.
Yet, as I said, it works OK from the console. What am I doing wrong?
The shell used by subprocess is probably not the shell you want. You can specify the shell with executable='path/to/executable'. Different shells have different quoting rules.
Even better might be to skip subprocess altogether, and write this as pure Python:
with open("c:foobar") as f_in:
with open("c:foobar.temp", "w") as f_out:
for line in f_in:
f_out.write(line.replace('&nbsp;', ' '))
I agree with Ned Batchelder's assessment, but think what you might want to consider using the following code because it likely does what you ultimately want to accomplish which can be done easily with the help of Python's fileinput module:
import fileinput
f = fileinput.input('C:foobar', inplace=1)
for line in f:
line = line.replace('&nbsp;', ' ')
print line,
f.close()
print 'done'
This will effectively update the given file in place as use of the keyword suggests. There's also an optional backup= keyword -- not used above -- which will save a copy of the original file if desired.
BTW, a word of caution about using something like C:foobar to specify the file name because on Windows it means a file of that name in whatever the current directory is on drive C:, which might not be what you want.
I think you'll find that, in Windows Python, it's not actually using the CygWin shell to run your command, it's instead using cmd.exe.
And, cmd doesn't play well with single quotes the way bash does.
You only have to do the following to confirm that:
c:\pax> echo hello >hello.txt
c:\pax> type "hello.txt"
hello
c:\pax> type 'hello.txt'
The system cannot find the file specified.
I think the best idea would be to use Python itself to process the file. The Python language is a cross-platform one which is meant to remove all those platform-specific inconsistencies, such as the one you've just found.

running a system command in a python script

I have been going through "A byte of Python" to learn the syntax and methods etc...
I have just started with a simple backup script (straight from the book):
#!/usr/bin/python
# Filename: backup_ver1.py
import os
import time
# 1. The files and directories to be backed up are specified in a list.
source = ['"C:\\My Documents"', 'C:\\Code']
# Notice we had to use double quotes inside the string for names with spaces in it.
# 2. The backup must be stored in a main backup directory
target_dir = 'E:\\Backup' # Remember to change this to what you will be using
# 3. The files are backed up into a zip file.
# 4. The name of the zip archive is the current date and time
target = target_dir + os.sep + time.strftime('%Y%m%d%H%M%S') + '.zip'
# 5. We use the zip command to put the files in a zip archive
zip_command = "zip -qr {0} {1}".format(target, ' '.join(source))
# Run the backup
if os.system(zip_command) == 0:
print('Successful backup to', target)
else:
print('Backup FAILED')
Right, it fails. If I run the zip command in the terminal it works fine. I think it fails because the zip_command is never actually run. And I don't know how to run it.
Simply typing out zip_command does not work. (I am using python 3.1)
Are you sure that the Python script is seeing the same environment you have access to when you enter the command manually in the shell? It could be that zip isn't on the path when Python launches the command.
It would help us if you could format your code as code; select the code parts, and click on the "Code Sample" button in the editor toolbar. The icon looks like "101/010" and if you hold the mouse pointer over it, the yellow "tool tip" box says "Code Sample <pre></pre> Ctrl+K"
I just tried it, and if you paste code in to the StackOverflow editor, lines with '#' will be bold. So the bold lines are comments. So far so good.
Your strings seem to contain backslash characters. You will need to double each backslash, like so:
target_dir = 'E:\\Backup'
This is because Python treats the backslash specially. It introduces a "backslash escape", which lets you put a quote inside a quoted string:
single_quote = '\''
You could also use a Python "raw string", which has much simpler rules for a backslash. A raw string is introduced by r" or r' and terminated by " or ' respectively. examples:
# both of these are legal
target_dir = r"E:\Backup"
target_dir = r'E:\Backup'
The next step I recommend is to modify your script to print the command string, and just look at the string and see if it seems correct.
Another thing you can try is to make a batch file that prints out the environment variables, and have Python run that, and see what the environment looks like. Especially PATH.
Here is a suggested example:
set
echo Trying to run zip...
zip
Put those in a batch file called C:\mytest.cmd, and then have your Python code run it:
result_code = os.system("C:\\mytest.cmd")
print('Result of running mytest was code', result_code)
If it works, you will see the environment variables printed out, then it will echo "Trying to run zip...", then if zip runs it will print a message with the version number of zip and how to run it.
zip command only work in linux not for windows.. thats why it make an error..

Categories