Execute bash commands that are within a list from python - python

i got this list
commands = ['cd var','cd www','cd html','sudo rm -r folder']
I'm trying to execute one by one all the elements inside as a bash script, with no success. Do i need a for loop here?
how to achieve that?, thanks all!!!!

for command in commands:
os.system(command)
is one way you could do it ... although just cd'ing into a bunch of directories isnt going to have much impact
NOTE this will run each command in its own subshell ... so they would not remember their state (ie any directory changes or environmental variables)
if you need to run them all in one subshell than you need to chain them together with "&&"
os.system(" && ".join(commands)) # would run all of the commands in a single subshell
as noted in the comments, in general it is preferred to use subprocess module with check_call or one of the other variants. however in this specific instance i personally think that you are in a 6 to 1 half a dozen to the other, and os.system was less typing (and its gonna exist whether you are using python3.7 or python2.5 ... but in general use subprocess exactly which call probably depends on the version of python you are using ... there is a great description in the post linked in the comments by #triplee why you should use subprocess instead)
really you should reformat your commands to simply
commands = ["sudo rm -rf var/www/html/folder"] note that you will probably need to add your python file to your sudoers file
also Im not sure exactly what you are trying to accomplish here ... but i suspect this might not be the ideal way to go about it (although it should work...)

This is just a suggestion, but if your just wanting to change directories and delete folders, you could use os.chdir() and shutil.rmtree():
from os import chdir
from os import getcwd
from shutil import rmtree
directories = ['var','www','html','folder']
print(getcwd())
# current working directory: $PWD
for directory in directories[:-1]:
chdir(directory)
print(getcwd())
# current working directory: $PWD/var/www/html
rmtree(directories[-1])
Which will cd three directories deep into html, and delelte folder. The current working directory changes when you call chdir(), as seen when you call os.getcwd().

declare -a command=("cd var","cd www","cd html","sudo rm -r folder")
## now loop through the above array
for i in "${command[#]}"
do
echo "$i"
# or do whatever with individual element of the array
done
# You can access them using echo "${arr[0]}", "${arr[1]}" also

Related

Replicate ". ./bash.sh" shell command in python script [duplicate]

I want to implement a userland command that will take one of its arguments (path) and change the directory to that dir. After the program completion I would like the shell to be in that directory. So I want to implement cd command, but with external program.
Can it be done in a python script or I have to write bash wrapper?
Example:
tdi#bayes:/home/$>python cd.py tdi
tdi#bayes:/home/tdi$>
Others have pointed out that you can't change the working directory of a parent from a child.
But there is a way you can achieve your goal -- if you cd from a shell function, it can change the working dir. Add this to your ~/.bashrc:
go() {
cd "$(python /path/to/cd.py "$1")"
}
Your script should print the path to the directory that you want to change to. For example, this could be your cd.py:
#!/usr/bin/python
import sys, os.path
if sys.argv[1] == 'tdi': print(os.path.expanduser('~/long/tedious/path/to/tdi'))
elif sys.argv[1] == 'xyz': print(os.path.expanduser('~/long/tedious/path/to/xyz'))
Then you can do:
tdi#bayes:/home/$> go tdi
tdi#bayes:/home/tdi$> go tdi
That is not going to be possible.
Your script runs in a sub-shell spawned by the parent shell where the command was issued.
Any cding done in the sub-shell does not affect the parent shell.
cd is exclusively(?) implemented as a shell internal command, because any external program cannot change parent shell's CWD.
As codaddict writes, what happens in your sub-shell does not affect the parent shell. However, if your goal is to present the user with a shell in a different directory, you could always have Python use os.chdir to change the sub-shell's working directory and then launch a new shell from Python. This will not change the working directory of the original shell, but will leave the user with one in a different directory.
As explained by mrdiskodave
in Equivalent of shell 'cd' command to change the working directory?
there is a hack to achieve the desired behavior in pure Python.
I made some modifications to the answer from mrdiskodave to make it work in Python 3:
The pipes.quote() function has moved to shlex.quote().
To mitigate the issue of user input during execution, you can delete any previous user input with the backspace character "\x08".
So my adaption looks like the following:
import fcntl
import shlex
import termios
from pathlib import Path
def change_directory(path: Path):
quoted_path = shlex.quote(str(path))
# Remove up to 32 characters entered by the user.
backspace = "\x08" * 32
cmd = f"{backspace}cd {quoted_path}\n"
for c in cmd:
fcntl.ioctl(1, termios.TIOCSTI, c)
I shall try to show how to set a Bash terminal's working directory to whatever path a Python program wants in a fairly easy way.
Only Bash can set its working directory, so routines are needed for Python and Bash. The Python program has a routine defined as:
fob=open(somefile,"w")
fob.write(dd)
fob.close()
"Somefile" could for convenience be a RAM disk file. Bash "mount" would show tmpfs mounted somewhere like "/run/user/1000", so somefile might be "/run/user/1000/pythonwkdir". "dd" is the full directory path name desired.
The Bash file would look like:
#!/bin/bash
#pysync ---Command ". pysync" will set bash dir to what Python recorded
cd `cat /run/user/1000/pythonwkdr`

How to keep a changed python working directory [duplicate]

I'm trying to write a small script to change the current directory to my project directory:
#!/bin/bash
cd /home/tree/projects/java
I saved this file as proj, added execute permission with chmod, and copied it to /usr/bin. When I call it by:
proj, it does nothing. What am I doing wrong?
Shell scripts are run inside a subshell, and each subshell has its own concept of what the current directory is. The cd succeeds, but as soon as the subshell exits, you're back in the interactive shell and nothing ever changed there.
One way to get around this is to use an alias instead:
alias proj="cd /home/tree/projects/java"
You're doing nothing wrong! You've changed the directory, but only within the subshell that runs the script.
You can run the script in your current process with the "dot" command:
. proj
But I'd prefer Greg's suggestion to use an alias in this simple case.
The cd in your script technically worked as it changed the directory of the shell that ran the script, but that was a separate process forked from your interactive shell.
A Posix-compatible way to solve this problem is to define a shell procedure rather than a shell-invoked command script.
jhome () {
cd /home/tree/projects/java
}
You can just type this in or put it in one of the various shell startup files.
The cd is done within the script's shell. When the script ends, that shell exits, and then you are left in the directory you were. "Source" the script, don't run it. Instead of:
./myscript.sh
do
. ./myscript.sh
(Notice the dot and space before the script name.)
To make a bash script that will cd to a select directory :
Create the script file
#!/bin/sh
# file : /scripts/cdjava
#
cd /home/askgelal/projects/java
Then create an alias in your startup file.
#!/bin/sh
# file /scripts/mastercode.sh
#
alias cdjava='. /scripts/cdjava'
I created a startup file where I dump all my aliases and custom functions.
Then I source this file into my .bashrc to have it set on each boot.
For example, create a master aliases/functions file: /scripts/mastercode.sh
(Put the alias in this file.)
Then at the end of your .bashrc file:
source /scripts/mastercode.sh
Now its easy to cd to your java directory, just type cdjava and you are there.
You can use . to execute a script in the current shell environment:
. script_name
or alternatively, its more readable but shell specific alias source:
source script_name
This avoids the subshell, and allows any variables or builtins (including cd) to affect the current shell instead.
Jeremy Ruten's idea of using a symlink triggered a thought that hasn't crossed any other answer. Use:
CDPATH=:$HOME/projects
The leading colon is important; it means that if there is a directory 'dir' in the current directory, then 'cd dir' will change to that, rather than hopping off somewhere else. With the value set as shown, you can do:
cd java
and, if there is no sub-directory called java in the current directory, then it will take you directly to $HOME/projects/java - no aliases, no scripts, no dubious execs or dot commands.
My $HOME is /Users/jleffler; my $CDPATH is:
:/Users/jleffler:/Users/jleffler/mail:/Users/jleffler/src:/Users/jleffler/src/perl:/Users/jleffler/src/sqltools:/Users/jleffler/lib:/Users/jleffler/doc:/Users/jleffler/work
Use exec bash at the end
A bash script operates on its current environment or on that of its
children, but never on its parent environment.
However, this question often gets asked because one wants to be left at a (new) bash prompt in a certain directory after execution of a bash script from within another directory.
If this is the case, simply execute a child bash instance at the end of the script:
#!/usr/bin/env bash
cd /home/tree/projects/java
echo -e '\nHit [Ctrl]+[D] to exit this child shell.'
exec bash
To return to the previous, parental bash instance, use Ctrl+D.
Update
At least with newer versions of bash, the exec on the last line is no longer required. Furthermore, the script could be made to work with whatever preferred shell by using the $SHELL environment variable. This then gives:
#!/usr/bin/env bash
cd desired/directory
echo -e '\nHit [Ctrl]+[D] to exit this child shell.'
$SHELL
I got my code to work by using. <your file name>
./<your file name> dose not work because it doesn't change your directory in the terminal it just changes the directory specific to that script.
Here is my program
#!/bin/bash
echo "Taking you to eclipse's workspace."
cd /Developer/Java/workspace
Here is my terminal
nova:~ Kael$
nova:~ Kael$ . workspace.sh
Taking you to eclipe's workspace.
nova:workspace Kael$
simply run:
cd /home/xxx/yyy && command_you_want
When you fire a shell script, it runs a new instance of that shell (/bin/bash). Thus, your script just fires up a shell, changes the directory and exits. Put another way, cd (and other such commands) within a shell script do not affect nor have access to the shell from which they were launched.
You can do following:
#!/bin/bash
cd /your/project/directory
# start another shell and replacing the current
exec /bin/bash
EDIT: This could be 'dotted' as well, to prevent creation of subsequent shells.
Example:
. ./previous_script (with or without the first line)
On my particular case i needed too many times to change for the same directory.
So on my .bashrc (I use ubuntu) i've added the
1 -
$ nano ~./bashrc
function switchp
{
cd /home/tree/projects/$1
}
2-
$ source ~/.bashrc
3 -
$ switchp java
Directly it will do: cd /home/tree/projects/java
Hope that helps!
It only changes the directory for the script itself, while your current directory stays the same.
You might want to use a symbolic link instead. It allows you to make a "shortcut" to a file or directory, so you'd only have to type something like cd my-project.
You can combine Adam & Greg's alias and dot approaches to make something that can be more dynamic—
alias project=". project"
Now running the project alias will execute the project script in the current shell as opposed to the subshell.
You can combine an alias and a script,
alias proj="cd \`/usr/bin/proj !*\`"
provided that the script echos the destination path. Note that those are backticks surrounding the script name.
For example, your script could be
#!/bin/bash
echo /home/askgelal/projects/java/$1
The advantage with this technique is that the script could take any number of command line parameters and emit different destinations calculated by possibly complex logic.
to navigate directories quicky, there's $CDPATH, cdargs, and ways to generate aliases automatically
http://jackndempsey.blogspot.com/2008/07/cdargs.html
http://muness.blogspot.com/2008/06/lazy-bash-cd-aliaes.html
https://web.archive.org/web/1/http://articles.techrepublic%2ecom%2ecom/5100-10878_11-5827311.html
In your ~/.bash_profile file. add the next function
move_me() {
cd ~/path/to/dest
}
Restart terminal and you can type
move_me
and you will be moved to the destination folder.
You can use the operator && :
cd myDirectory && ls
While sourcing the script you want to run is one solution, you should be aware that this script then can directly modify the environment of your current shell. Also it is not possible to pass arguments anymore.
Another way to do, is to implement your script as a function in bash.
function cdbm() {
cd whereever_you_want_to_go
echo "Arguments to the functions were $1, $2, ..."
}
This technique is used by autojump: http://github.com/joelthelion/autojump/wiki to provide you with learning shell directory bookmarks.
You can create a function like below in your .bash_profile and it will work smoothly.
The following function takes an optional parameter which is a project.
For example, you can just run
cdproj
or
cdproj project_name
Here is the function definition.
cdproj(){
dir=/Users/yourname/projects
if [ "$1" ]; then
cd "${dir}/${1}"
else
cd "${dir}"
fi
}
Dont forget to source your .bash_profile
This should do what you want. Change to the directory of interest (from within the script), and then spawn a new bash shell.
#!/bin/bash
# saved as mov_dir.sh
cd ~/mt/v3/rt_linux-rt-tools/
bash
If you run this, it will take you to the directory of interest and when you exit it it will bring you back to the original place.
root#intel-corei7-64:~# ./mov_dir.sh
root#intel-corei7-64:~/mt/v3/rt_linux-rt-tools# exit
root#intel-corei7-64:~#
This will even take you to back to your original directory when you exit (CTRL+d)
I did the following:
create a file called case
paste the following in the file:
#!/bin/sh
cd /home/"$1"
save it and then:
chmod +x case
I also created an alias in my .bashrc:
alias disk='cd /home/; . case'
now when I type:
case 12345
essentially I am typing:
cd /home/12345
You can type any folder after 'case':
case 12
case 15
case 17
which is like typing:
cd /home/12
cd /home/15
cd /home/17
respectively
In my case the path is much longer - these guys summed it up with the ~ info earlier.
As explained on the other answers, you have changed the directory, but only within the sub-shell that runs the script. this does not impact the parent shell.
One solution is to use bash functions instead of a bash script (sh); by placing your bash script code into a function. That makes the function available as a command and then, this will be executed without a child process and thus any cd command will impact the caller shell.
Bash functions :
One feature of the bash profile is to store custom functions that can be run in the terminal or in bash scripts the same way you run application/commands this also could be used as a shortcut for long commands.
To make your function efficient system widely you will need to copy your function at the end of several files
/home/user/.bashrc
/home/user/.bash_profile
/root/.bashrc
/root/.bash_profile
You can sudo kwrite /home/user/.bashrc /home/user/.bash_profile /root/.bashrc /root/.bash_profile to edit/create those files quickly
Howto :
Copy your bash script code inside a new function at the end of your bash's profile file and restart your terminal, you can then run cdd or whatever the function you wrote.
Script Example
Making shortcut to cd .. with cdd
cdd() {
cd ..
}
ls shortcut
ll() {
ls -l -h
}
ls shortcut
lll() {
ls -l -h -a
}
If you are using fish as your shell, the best solution is to create a function. As an example, given the original question, you could copy the 4 lines below and paste them into your fish command line:
function proj
cd /home/tree/projects/java
end
funcsave proj
This will create the function and save it for use later. If your project changes, just repeat the process using the new path.
If you prefer, you can manually add the function file by doing the following:
nano ~/.config/fish/functions/proj.fish
and enter the text:
function proj
cd /home/tree/projects/java
end
and finally press ctrl+x to exit and y followed by return to save your changes.
(NOTE: the first method of using funcsave creates the proj.fish file for you).
You need no script, only set the correct option and create an environment variable.
shopt -s cdable_vars
in your ~/.bashrc allows to cd to the content of environment variables.
Create such an environment variable:
export myjava="/home/tree/projects/java"
and you can use:
cd myjava
Other alternatives.
Note the discussion How do I set the working directory of the parent process?
It contains some hackish answers, e.g.
https://stackoverflow.com/a/2375174/755804 (changing the parent process directory via gdb, don't do this) and https://stackoverflow.com/a/51985735/755804 (the command tailcd that injects cd dirname to the input stream of the parent process; well, ideally it should be a part of bash rather than a hack)
It is an old question, but I am really surprised I don't see this trick here
Instead of using cd you can use
export PWD=the/path/you/want
No need to create subshells or use aliases.
Note that it is your responsibility to make sure the/path/you/want exists.
I have to work in tcsh, and I know this is not an elegant solution, but for example, if I had to change folders to a path where one word is different, the whole thing can be done in the alias
a alias_name 'set a = `pwd`; set b = `echo $a | replace "Trees" "Tests"` ; cd $b'
If the path is always fixed, the just
a alias_name2 'cd path/you/always/need'
should work
In the line above, the new folder path is set
This combines the answer by Serge with an unrelated answer by David. It changes the directory, and then instead of forcing a bash shell, it launches the user's default shell. It however requires both getent and /etc/passwd to detect the default shell.
#!/usr/bin/env bash
cd desired/directory
USER_SHELL=$(getent passwd <USER> | cut -d : -f 7)
$USER_SHELL
Of course this still has the same deficiency of creating a nested shell.

Use Python in ubuntu to show contents of a directory

I have a .py file in the home directory which contains these three lines:
import os
os.system("cd Desktop/")
os.system("ls")
and I want it to "ls" from the "Desktop" directory but it shows contents of the /home directory.
I looked at these pages:
Calling an external command in Python
http://ubuntuforums.org/showthread.php?t=729192
but I could not understand what to do. Can anybody help me?
The two calls are separate from each other. There is no context kept between successive invocations of os.system because a new shell is spawned for every call. First os.system("cd Desktop/") switches directories to Desktop and exits. Then a new shell executes ls in the original folder.
Try chaining your commands with &&:
import os
os.system("cd Desktop/ && ls")
This will show the contents of directory Desktop.
Fabric
If your application is going to be heavy on os usage you might consider using python-fabric. It allows you to use higher level language constructs like contextmanagers to make command line invocations easier:
from fabric.operations import local
from fabric.context_managers import lcd
with lcd("Desktop/"): # Prefixes all commands with `cd Desktop && `
contents=local("ls", capture=True)
You have to consider that os.system executes the commands in a sub-shell. Hence 1) python starts a sub-shell, 2) the directory is changed, 3) then the sub-shell is completed, 4) return to previous state.
To force the current directory change you should do:
os.chdir("Desktop")
Always try and do it by other means that through os.system (os.listdir), or also by doing other than subprocess (which is an excellent module for command control in the shell)

turn all files in a folder into function aliases with bash

I have a folder of python files. I want to turn them into functions I can call in bash. This following one-liner is in my profile text file loading at bash login, but it doesn't work. How can I fix it?
for i in `ls ~/Dropbox/Documents/tools/python`; do
fullfilename=$(basename "$i");
filename="${fullfilename%.*}";
$("alias $filename='/usr/local/bin/python3.3 ~/Dropbox/Documents/tools/python/$fullfilename '");
done
EDIT:
per Liviu Chircu's recommendation, I removed the " before and after the alias command. Following is now the new code:
for i in `ls ~/Dropbox/Documents/tools/python`; do
fullfilename=$(basename "$i");
filename="${fullfilename%.*}";
$(alias $filename='/usr/local/bin/python3.3 ~/Dropbox/Documents/tools/python/$fullfilename ');
done
Now I get this error:
$ findfactors 40
-bash: findfactors: command not found
Three steps to make the Python scripts executable from bash.
First, at the top of every file in that directory, add the following line to tell bash which interpreter to use.
#!/usr/local/bin/python3.3
Second, set all those files to executable.
chmod u+x ~/Dropbox/Documents/tools/python/*
Third, add that directory to your PATH. This one goes in ~/.bashrc, while the other two are one-time things.
export PATH=$PATH:$HOME/Dropbox/Documents/tools/python
Looks like you need to remove " from your alias command.
Try
$(alias $filename='/usr/local/bin/python3.3 ~/Dropbox/Documents/tools/python/$fullfilename ');
Instead of
$("alias $filename='/usr/local/bin/python3.3 ~/Dropbox/Documents/tools/python/$fullfilename '");
You mean something like
for i in "$HOME"/Dropbox/Documents/tools/python/*; do
fullfilename="$(basename "$i")"
filename="${fullfilename%.*}"
alias "$filename"="/usr/local/bin/python3.3 ~/Dropbox/Documents/tools/python/$fullfilename"
done

Why can't bash see my files?

I have a simple bash script that allows cron to execute a series of Python scripts in a virtualenv. The script keeps raising No such file or directory errors.
~/nightly.sh works fine:
#!/bin/bash
source virt_env/myproject/bin/activate
cd virt_env/myproject/main
python script1.py
python script2.py
I want to keep everything in ~/virt_env/myproject/main/ to simplify deployment. I thought I could call bash virt_env/myproject/main/nightly.sh on this:
#!/bin/bash
MAINDIR=`dirname $0`
cd $MAINDIR
source ../bin/activate
python script1.py
python script2.py
but I get the No such file or directory. If I manually cd to ~/virt_env/myproject/main/, then I can run the main commands no problem. Clearly I'm missing something about how dirname and cd work in this context.
How can I point bash at the right place?
Solution
As proposed in the accepted answer, it's best to avoid calling cd from within the script and use an explicit path variable instead. Here's the working version of virt_env/myproject/main/nightly.sh:
#!/bin/bash
MAINDIR=`dirname $0`
echo "The main directory is" $MAINDIR
# Activate virtual environment
source $MAINDIR/../bin/activate
# Run Python scripts
python $MAINDIR/python1.py
python $MAINDIR/python2.py
Because the Python scripts are now called from an arbitrary path, I needed to update the python scripts to be smarter about path awareness as well.
This code fails because os.path.basename omits path information:
# works when called with "python python1.py"
# fails when called with "python $MAINDIR/python1.py"
CONFIG_FILE = os.path.basename(__file__)[:-3] + ".config"
f = open(CONFIG_FILE,"r")
Updating it to use os.path.abspath fixes the problem:
# works regardless of how it is called
CONFIG_FILE = os.path.abspath(__file__)[:-3] + ".config"
f = open(CONFIG_FILE,"r")
Perhaps it would simply be better to eliminate the 'cd' command. Invoke everything from a full path specification. In your example add $MAINDIR/ to the executables.
Your bash script can then be in any directory where the executables are reachable. You are not exposed to the problems of what happens when cd fails.
Example:
cd yourdir
rm -f yourglob # oops things got removed from where you started if yourdir did not exist.
Two things:
Are you sure you know what the dirname command is doing? It's going to remove the top-level directory as well as the leading slash on whatever you call it on. I would make absolutely sure that the output of dirname is exactly what you think it is.
For example, /home/user/ will output /home.
You're using ~, which references the $HOME variable in your environment. You didn't mention where the cron is listed, but make sure it's not being run as a different user. Root's ~ and your ~ will be two completely different directories.
That's all I can think of. I hope that helps!
Add echo $MAINDIR after
MAINDIR=`dirname $0`
cd $MAINDIR
So you can see, if the contents of MAINDIR is correct.
Also you can run sh with -x or add set -x to the beginning of the script to see what happens.

Categories