Bash can't find right directory in script - python

I'm making a compress script for my text editor, and it's all working up to the part where it needs to make the file Run. Inside of Run is just this code: python ./App.pyc. When I run the program by double-clicking on it in Finder, it says that it can't open file './App.pyc' [Errno 2] No such file or directory within Terminal.
And if I run it through Terminal after I've cd'd to the directory Run and App.pyc are in, it works. I'm assuming this is because we aren't in the right directory.
My question is, how can I make sure Run is being ran in the right directory? If I put cd in it, it'll work, but then if somebody moves the folder elsewhere it won't work anymore.
#!/usr/bin/python
### Compresser script.
# Compress files.
import App
import Colors
# Import modules
import os
# Clear the folder to put the compressed
# files in (if it exists).
try:
os.system('rm -rf BasicEdit\ Compressed')
except:
pass
# Remake the folder to put compressed files in.
os.system('mkdir BasicEdit\ Compressed')
# Move the compiled files into the BasicEdit
# Compressed folder.
os.system('mv App.pyc BasicEdit\ Compressed/')
os.system('mv Colors.pyc BasicEdit\ Compressed/')
# Create contents of run file.
run_file_contents = "python ./App.pyc\n"
# Write run file.
run_file = open("./BasicEdit Compressed/Run", 'w')
run_file.write(run_file_contents)
# Give permissions of run file to anybody.
os.system('chmod a+x ./BasicEdit\ Compressed/Run')
# Finally compress BasicEdit, and remove the old
# folder for BasicEdit Compressed.
os.system('zip -9r BasicEdit.zip BasicEdit\ Compressed')
os.system('rm -rf BasicEdit\ Compressed')
(PS, what's [Errno 1]? I've never seen it before.)

The Python script's current working directory can be modified with the os.chdir() call, after which references to . will be correct.
If you want to find the location of the source file currently being run rather than hardcoding a directory, you can use:
os.chdir(os.path.dirname(__file__))
The bash equivalent to this logic is:
cd "${BASH_SOURCE%/*}" || {
echo "Unable to change directory to ${BASH_SOURCE%/*}" >&2
exit 1
}
See BashFAQ #28 for more details and caveats.

As developed above together with #William Purcell, you have to retrieve the absolute path by os.pwd() and then use the absolute path for the python call.
I withdraw my proposal and go with #Charles Duffy's answer. However, I don't delete my attempt as the comments seem to be useful to others!

Related

Run Python Script From Script Directory/Current Directory

[Introduction]
Hi! I'm working on a python script to automate installation of UWP-Apps, it's been a long time i'm not touching Python; until this day. The script uses Depedencies inside the script directory, i've looking up on my older scripts and found this specific code.
os.chdir(os.path.dirname(sys.argv[0]))
[Problematic]
However, using the above code doesn't work on my current script but it's working fine on older scripts. When using above, it shows:
OSError: [WinError 123] The filename, directory name, or volume label syntax is incorrect: ''
Already looking up on Internet about this topic; but most of them was talking about running the script from outer/different directory that leads me to dead end.
Any helps is appreciated :)
The easiest answer is probably to change your working directory, then call the .py file from where it is:
cd path/to/python/file && python ../.py
Of course you might find it even easier to write a script that does it all for you, like so:
Save this as runPython.sh in the directory where you're running the python script from, is:
#!/bin/sh
cd path/to/python/file
python ../script.py
Make it executable (for yourself):
chmod +x ./runPython.sh
Then you can simply enter your directory and run it:
./runPython.sh
If you want to only make changes to the python script:
mydir = os.getcwd() # would be the folder where you're running the file
mydir_tmp = "path/to/python/file" # get the path
mydir_new = os.chdir(mydir_tmp) # change the current working directory
mydir = os.getcwd()
The reason you got an error was because sys.argv[0] is the name of the file itself, instead you can pass the directory as a string and use sys.argv[1] instead.
import os
from os.path import abspath, dirname
os.chdir(dirname(abspath(__file__)))
You can use dirname(abspath(__file__))) to get the parent directory of the python script and os.chdir into it to make the script run in the directory where it is located.

Downloading all jupyter notebooks from Coursera [tar size exeeding 100MB]

As mentioned in the coursera help articles in order to download notebooks from the class we need to zip all the content of root folder into single file and download the final workspace.tar.gz using these steps: but it is not working all courses.
Anyone knows proper way to do this !!
Open the home folder of your coursera jupyter notebook:
you can do this by opening any of the course notebooks and thanm selecting file> open or by clicking on Jupyter icon at the top left corner of notebook.
Open terminal inside the notebook:
On the home page of your notebooks, at the top left corner select new> terminal
Check in which dir you are:
this is important as different courses have their materials in different dir!
Some courses have a dir name jovyan and inside that you have two folders generally work and work-ro.
in work you have your actual content that you can see on your notebook home page.
in work-ro you have only read_only folder. This same folder you have it in your work dir but you cant open the content of that folder after downloading! (I dont know why I cant open it)
I turns out that this folder contains images which are in your notebooks. that is the reason you will have to zip both these folders.
Its not necessary that all the course have this folder named work!
In some courses materials are directly inside root dir. In such cases you can find the directory with your material by finding folder name ending with -ro
Ex in one of my course I located a folder named TF-ro and there was another folder named TF containing all course material! As per above pattern TF-ro contained read_only folder.
Just in case you are wondering how to navigate inside terminal: [Use these commands]
ls: list everything inside the folder
cd: to change the folder you are currently in
Ex: cd .. #go to previous folder cd <dirname> #go to that specified folder
compress both the folders using tar:
Navigate to the folder which contains both of these folders i.e work and work-ro or if you read my second case than Tf and TF-ro or folders in your case.
Use this to make tar file:
Use this when your folder contains only two dirs that you want
tar -czvf <choose a name>.tar.gz <address of dir to compress>
Ex: tar -czvf data.tar.gz ./
use this when you are in root folder and you have multiple dir along with the folders you want
tar -czvf <choose a name>.tar.gz <dir1 addres> <dir2 addres>
Ex: tar -czvf data.tar.gz ./work ./work-ro
Just in case you are wondering!
./ means current folder.
Check the size of your tar file:
This is also important!!
If your process of making tar file is taking too long or your terminal appears to be frozen ! than there are some big files in your home folder.
You can check the size of your tar file using: ls -lh data.tar.gz.
Normally the size should not be more than 10 - 15 Mbs.
If your size is in GBs than you are mostly downloading large amount of datasets and csv files!
you cannot download big files like this!
[Workaround for this problem are mentioned below]
run this command: du
This will list all the dir's and the size of dir's in current folder.
Figure out which folder has more size.
Note: size shown in this commands are in Number of sections occupied 1 section = 1024 bytes
Exclude these folder wile making tar...
In order to remove previous tar file run rm data.tar.gz
make the tar like this:
tar -czvf <yourName>.tar.gz --exclude=<address to exclude> <dir/dirs to zip>
Ex: tar -czvf data.tar.gz --exclude=./work/data --exclude=./work/- ./work ./work-ro
Move the file :
You can only see the content in the work folder (or any other folder your content is in) on your class's notebook home folder.
This is why we will move over tar file to that folder.
move using this command mv <file name> <location> Ex :mv data.tar.gz ./work
Download your file:
Now you can see your file in your home folder in your browser. simply select the file you will see download option available at the top !!
Sometimes you dont see the download button on the top, in such cases...
right click your file> save link As> then save it with .tar.gz extension
Just to confirm check the size of file you have downloaded and one in your classroom!!
Work Around for downloading big data sets:
Your course generally does not use all the csv's or data sets that it has stored in the data folder. When you do the assignments see which files are / data sets are used and download only those manually. i.e opening that file on your classroom and downloading it using using file> download
if you still want the entire thing than make separate tar file of that folder only. Than split the tar file (you will find it online easily) and than download as I have mentioned earlier!
After the download it is necessary to concatenate the files:
cat allfiles.tar.gz.part.* > allfiles.tar.gz
I would suggest not to waste time in doing this!! Just download what is required and that's it!!
I hope this was helpful !! cauz I spent 5 hr figuring out how to do it !! ENJOY !!
Alternatively, you could initialize a git repo and push it to your GitHub account.
Open terminal (Jupiter home > new > terminal)
Run the following code: (I'm assuming you've already created a GitHub repo, if not create one and then do the following; you'll need the link to your repo)
git init
git config --global user.name "test"
git config --global user.email "test"
git add -A; git commit -m "commit"
git remote add origin <_your-github-repo-url_>
git push origin master -u --verbose
You can just compress all the programming exercise (notebook + data) by placing this commands at the beginning of your notebook:
import os
!tar chvfz notebook.tar.gz *
print("File size: " + str(os.path.getsize("notebook.tar.gz")/1e6) + " MB")
if os.path.getsize("notebook.tar.gz")/1e6 >100 :
print("Splitting file")
!split -b 100M notebook.tar.gz "notebook.tar.gz."

Permission denied on shutil.move on image files

I am trying to move some files around. I can move any extension type except .png, .jpg, or .gif. When I try to move those types of files I get "IOError: [Errno 13] Permission denied" even though I am the admin. Code below
import os, glob, shutil
dir = r'C:\\Users\\jcan4\\Desktop\\testmove\\*'
print(dir)
files = glob.glob(dir)
files.sort(key=os.path.getmtime)
for i, file in enumerate(files, start=1):
print(file)
oldext = os.path.splitext(file)[1]
shutil.move(file, 'Attachment-%s' % (i) + oldext)
First things first, you're double escaping your dir variable:
print(r'C:\\Users\\jcan4\\Desktop\\testmove\\*')
# Yields 'C:\\\\Users\\\\jcan4\\\\Desktop\\\\testmove\\\\*' !!
# What you really meant was either one of the following:
dir_harderToRead = 'C:\\Users\\jcan4\\Desktop\\testmove\\*'
dir_easyToRead = r'C:\Users\jcan4\Desktop\testmove\*'
If you are still experiencing the error, it's because you are not giving the python script permissions to move the file. There are a couple ways to get around this:
Windows
(This applies to the asked question)
Open command prompt (I see your file path and am assuming you're on windows) with administrative rights. (see here)
Change ownership of the images to you. (see here for windows 10 or here for windows 7)
Linux (MacOS)
(This applies to people on Linux that may have the same problem)
Run the python script with root privileges:
# At command line
sudo python your_script_name.py
Change ownership of file to yourself:
# At command line
# Changes ownership of entire directory (CAREFUL):
chmod 755 /absolute/path/to/dir
chmod 755 relative/path/to/dir
# Or you can change file by file:
chmod 755 /absolute/path/to/file
chmod 755 relative/path/to/file
For more info, I used this site on permissions. (If someone has a numerical value than 755 for chmod please say so.)

Run bash script to automatically copy contents from USB device after USB device mount

I have a python script to copy all files from a USB storage device to a target folder in my Ubuntu machine. I have never programmed in Python before.
import os
import shutil
from shutil import copytree, ignore_patterns
files = os.listdir('/media/user/HP drive')
destination = '/home/user/Documents/Test/%s'
try :
for f in files:
source = '/media/user/HP drive/%s' % f
copytree(source, destination, ignore=ignore_patterns('*.pyc', 'tmp*'))
except Exception as e:
print(e)
The above script runs fine but it creates a folder %s inside the Test folder with the lock symbol. When I remove the %s and just use
destination = '/home/user/Documents/Test/'
It gives me [Errorno 17] file exists.
This is the bash script(copy.sh) which I want to run when a USB device is mounted.
#!/bin/sh
python /var/www/html/copy_flash.py #This doesn't work.
# echo "My message" > /var/www/html/thisisaverylongfilename.txt #This works
So the python command is not working but the echo command does when I plug in a USB.
Here's the line I added in /etc/udev/rules.d/test.rules
ACTION=="add",KERNEL=="sdb*", RUN+="/var/www/html/copy.sh"
Is it because the USB drive is not ready when the bash script runs?
How do I copy the USB drive contents in a regular folder and not in %s?
How do I actually copy the contents?
In order to not use %s you can use the format method.
source = '/media/users/HP/{path}'.format(path=your_filename_here)
You can use any name within the braces which will create the keyword arguments of format. You can also use numbers which are converted to positional arguments.
An example:
'Hello {0}! Good {1}'.format('DragonBorn', 'Evening!')
copytree from shutil also requires that the destination not exist. So you will need to check if the destination exists and remove it if it does. You can use os.rmdir and os.path.exists for that. shutil may also have an equivilent function.
https://docs.python.org/3.5/library/shutil.html#shutil.copytree
You can do this check and copy the tree with:
if os.path.exists(destination):
if os.listdir(): # If the directory is not empty, do not remove.
continue
os.rmdir(destination)
shutil.copytree(source, destination)
If you want to remove the entire tree under the directory you can use shutil.rmtree().
if os.path.exists(destination):
shutil.rmtree(destination)
Solution for 1: How do I copy the USB drive contents in a regular folder and not in %s?
I made it to work from Ethan's answer.
Solution for 2: How do I actually copy the contents?
Okay so I found out about systemd from this answer and the advantage it has over udev rule is that the script really fires after mount, not after adding system device which is why my python script was unable to copy the files because the script was running before the device was actually mounted.
I removed the file /etc/udev/rules.d/test.rules
and created a new file in /etc/systemd/system/copy.service with the contents:
[Unit]
Description=My flashdrive script trigger
Requires=media-YourMediaLabel.mount
After=media-YourMediaLabel.mount
[Service]
ExecStart=/home/you/bin/triggerScript.sh
[Install]
WantedBy=media-YourMediaLabel.mount
Run this command sudo systemctl list-units -t mount. Find your device and replace media-YourMediaLabel.mount above with your device unit.
Then you have to start/enable the service:
sudo systemctl start copy.service
sudo systemctl enable copy.service
And that's it. Your USB device's content will be automatically copied in your target destination after it's mounted.
Give the full path of python like:
/usr/bin/python /var/www/html/copy_flash.py

Crontab Python Script Execution (Can't find settings config file)

My Crontab -l
# m h dom mon dow command
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
00 8,20 * * * python /home/tomi/amaer/controller.py >>/tmp/out.txt 2>&1
My controller.py has config file settings.cfg also it uses other script in the folder it's located (I chmoded only controller.py)
The error
1;31mIOError^[[0m: [Errno 2] No such file or directory: 'settings.cfg'
I have no idea how to fix this? Please help me?
Edit: The part that read the config file
def main():
config=ConfigParser.ConfigParser()
config.readfp(open("settings.cfg"),"r")
As I initially wrote in my comment, this is because you are using relative path to the current working directory. However, that is not going to be the same when running all this via the cron executable rather than the python interpreter directly via the shebang.
Your current code would look for the "settings.cfg" in the current working directory which is where the cron executable resides, and not your script. Hence, you would need to change your code logic to using absolute paths by the help of the "os" built-in standard module.
Try to following line:
import os
...
def main():
config = ConfigParser.ConfigParser()
scriptDirectory = os.path.dirname(os.path.realpath(__file__))
settingsFilePath = os.path.join(scriptDirectory, "settings.cfg")
config.readfp(open(settingsFilePath,"r"))
This will get your the path of your script and then appends the "settings.cfg" with the appropriate dir separator for your operating system which is Linux in this particular case.
If the location of the config file changes any time in the future, you could use the argparse module for processing a command line argument to handle the config location properly, or even without it simply just using the first argument after the script name like sys.argv[1].
Your code is looking for settings.cfg in its current working directory.
This working directory will not be the same when cron executes the job, hence the error
You have two "easy" solutions:
Use an absolute path to the config file in your script (/home/tomi/amaer/config.cfg)
CD to the appropriate directory first in your crontab (cd /home/tomi/amaer/ && python /home/tomi/amaer/controller.py)
The "right" solution, though, would be to pass your script a parameter (or environment variable) that tells it where to look for the config file.
It's not exactly good practice to assume your config file will always be lying just next to your script.
You might want to have alook at this question: https://unix.stackexchange.com/questions/38951/what-is-the-working-directory-when-cron-executes-a-job

Categories