This question already has answers here:
Make a Bash alias that takes a parameter?
(24 answers)
Closed 4 months ago.
I am trying to create an alias in my bash profile such that I can call
backup_dir Playground
and have it complete:
cp -r Playground $HOME/BACKUP
I would also like to ensure that if the file/directory is of the same name it is just overwritten.
The code I input in my bash_profile is as follows:
alias backup='cp $2 $HOME/BACKUP/$2'
alias backup_dir='cp -r $2 $HOME/BACKUP/$2'
I also tried it with $1 but it did not work either.
What actually occurs is that it copies the contents of my backup directory and creates it in the directory I'm supposed to be copying/copying from.
Aliases don't really take arguments, and trying to define aliases as if they did winds up in strange territory (if you define a simple alias 'foo' that just echos instead of copies, you'll find it reversed the order of the arguments you passed - i.e. the first argument is the backup directory and the second is the directory you wanted.) I'm guessing the details of why that happens aren't super interesting to you - but that is... 'expected'.
The solution is to write a function to use in your alias, or to write a small shell script and call that instead.
Aliases really substitute 'bash' as the command that then executes the string you pass (if you echo $0 instead of $1, you'll see something like 'bash foo' as the output).
More detailed instructions can be found in the ubuntu forums (for example)
Related
I am new to UNIX and have a homework assignment that is giving me trouble. I am to write a script that will back up specified files from the current directory into a specified destination directory. This script is to take three arguments.
sourcePath, which is the path to the source files/files being backed up or copied.
backupPath, which is the path to the target directory where the files will be backed up.
filePrefix, which is used to identify which files to backup, specifically only files whose names begin with the given prefix will be copied while others will be ignored. Example would be, if the user enters the letter "d", then all files starting with that letter are to be copied while any other file is to be ignored.
I haven't learned much about scripting/functions in bash so I've tried looking up tutorials which have been helpful but not enough. This script is something I can easily do when just typing out the commands. For instance, I would cd into the target directory that has the files, then using the cp command copy files that begin with the specific prefix to the target directory, but when making a script I am at a dead end.
I feel as though my code is monumentally incorrect and its due to my lack of experience, but nothing online has been of any help. So far my code is
read sourcePath
read backupPath
read filePrefix
grep /export/home/public/"$sourcePath
mkdir -p $backupPath
cp /export/home/public/"$sourcePath"/$filePrefix /home/public/"$backupPath"
So an example execution of the script would be
$ ./script.sh
(sourcePath)HW4testdir (backupPath)backup (filePrefix)d
Output:
backing up: def (example file starting with d)
backing up: dog (example file starting with d)
So far when executing the code, nothing happens. Again, I'm sure most, or even all of the code is wrong and totally off base, but I never learned about scripting. If I did not have to create a script, I could easily achieve this desired outcome.
I suggest with bash:
read -r -p "sourcePath: " sourcePath
read -r -p "backupPath: " backupPath
read -r -p "filePrefix: " filePrefix
mkdir -p /home/public/"$backupPath"
cp /export/home/public/"$sourcePath/$filePrefix"* /home/public/"$backupPath"
Make sure that the used user has the right to create the directory /home/public/"$backupPath".
See: help read
For a start: Your assignment states, that your script should accept arguments.
However your script does not take arguments. It reads the parameters from standard input. Arguments are passed to the script on the command line, and your script would be called as
./script.sh HW4testdir backup d
Hence you can't use read to fetch them. The first argument is available under the name $1, the second argument is $2 and so on. You could write for instance
sourcePath=${1?Parameter missing}
which has the side effect to abort the script with an error message, if the caller forgets to pass the parameter.
Another point: You don't say anywhere that bash should be used to run the script. Since you want the script to be called by
./script.sh ....
and not by
bash ./script.sh ....
you must encode the information, that bash should be used, in your script. Assuming that your bash is located in /usr/bin, you would do this by making the first line of the script
#!/usr/bin/bash
This question already has answers here:
Global environment variables in a shell script
(7 answers)
Closed 2 years ago.
I have a seemingly simple bash script to setup my environment:
The first two lines are:
#!/bin/bash
export CVE_ENV_DIR=$PWD
easy, hey? Well, see what happens when I run it, I get the following output:
$ echo $PWD
/work/env
$ ./env.sh
$ echo $CVE_ENV_DIR
$
Why does CVE_ENV_DIR not get set to /work/env? What is happening here? When I type export CVE_ENV_DIR=$PWD manually on the shell, it works as expected...
Child shells cannot affect the environment of their parent. If you want the script to affect the parent's environment, you need to:
source ./env.sh
So what's going on? When you run a bash script, either by bash env.sh or env.sh, you're spawning a program with its own environment, an environment that's divorced from its parent. But, when you run the commands contained in the script at the command line, or using source, there is no spawned environment.
Edit to address #syme's comment. Bash scripts meant to be read using source are often pure configuration, containing only assignments and calculations. It's possible to also make them a little more helpful and self-documenting with a clever she-bang hack like:
#!/bin/echo USAGE: source
# Default configuration file for the Frobnicator package.
FOO=bar
BAR=$(stat /baz)
[[ -f /baz ]] && BAZ=file || BAZ=
export FOO BAR BAZ
Making a bash script meant for configuration look like a configuration script, you help future maintainers. You also help yourself my modularizing your script code into distinct parts, each part with its one unique function.
As a side note, please don't export on the same line as you assign.
This question already has answers here:
Why can't I change directories using "cd" in a script?
(33 answers)
Closed 7 years ago.
A while ago I wrote a script (let's call it myScript) so that it would cd into a specific folder. I saved it in cygwin home directory so I could run it just by calling ~/myScript. I had problems with it, but got it working by calling . ~/myScript
I have written a parent script (let's call it parentScript) that does few tasks and wanted to add an option so it would cd to my specific directory by calling myScript inside it.
myScript looks something like this:cd /cygdrive/c/Users/USER_NAME
parentScript has this:
if [ "${1}" -eq CD_TO_USER_NAME_OPTION ]; then
. ~/myScript
fi
This gives me the same problem I had before with myScript. It cds in the subshell, but then exits leaving me in the directory I started with. I want to be able to run myScript without using the parent that's why I didn't put in parentScript (like grep -E and Egrep).
What am I doing wrong??
You would need to invoke parentScript by sourcing it as well:
. parentScript
Then, in whatever script contains that, you would need to make sure the first argument is
./grandparentScript CD_TO_USER_NAME_OPTION
A script invoked by any other means besides sourcing is run in a new process. A process has its own current working directory (man 3 getcwd). That directory is inherited from the parent process, but the parent doesn't get it from the child when the child exits. The only way to have an inner script change the working directory of an outer script is by running them in the same process. That is done most simply by sourcing, or the . command, as you've discovered.
Another solution would be to use a shell function for your directory change:
magicCd() {
cd my/special/place
}
However, to avoid mixing procedural code with data, maybe the best choice would be simply to use the builtin cd command and store the desired destination in a variable.
my_special_place="$HOME/my/special/place"
cd "$my_special_place"
This last is just as abstract as the sourced script, function or alias, and much more obvious to any maintenance programmer who comes along.
This would be better done via an alias than a shell script:
$ alias myscript="cd /to/some/directory"
Then executing myscript will put you in that directory:
$ myscript
$ pwd
/to/some/directory
This question already has answers here:
Getting "command not found" error in bash script
(6 answers)
Closed 2 years ago.
I've created a simple script to check if a folder exists and if not to create it. The script that follow
#!/bin/bash
PATH=~/Dropbox/Web_Development/
FOLDER=Test
if [ ! -d $PATH$FOLDER ]
then
echo $PATH$FOLDER 'not exists'
/bin/mkdir $PATH$FOLDER
echo $PATH$FOLDER 'has been created'
fi
works only if the mkdir command is preceded by /bin/. Failing in that, bash env output the error message "command cannot be found".
I though this could have been related to the system $PATH variable, but it looks regular (to me) and the output is as following:
/Library/Frameworks/Python.framework/Versions/2.7/bin:/bin:/usr/local/bin:/usr/bin:/sbin:/usr/local/sbin:/usr/sbin
I'm not sure whether the order with the different bin folders have been listed make any difference, but the /bin one (where the mkdir on my OSX Maverick) seems to reside is there hence I would expect bash to being able to execute this.
In fact, if I call the bash command from terminal, by typing just mkdir bash output the help string to suggest me how the mkdir command should be used. This suggests me that at a first instance bash is able to recognise the $PATH variable.
So what could be the cause? Is there any relation between the opening statement at the top of my .sh - #!/bin/bash - file and the "default" folder?
Thanks
Yeah, sometimes it is a bad idea to use capital letters for constant variables, because there are some default ones using the same convention. You can see some of the default variables here (Scroll to Special Parameters and Variables section). So it is better to use long names if you don't want to get any clashes.
Another thing to note is that you're trying to replicate mkdir -p functionality, which creates a folder if it does not exist (also it does create all of the parents, which is what you need in most cases)
One more thing - you always have to quote variables, otherwise they get expanded. This may lead to some serious problems. Imagine that
fileToRemove='*'
rm $fileToRemove
This code will remove all files in the current folder, not a file named * as you might expect.
One more thing, you should separate path from a folder with /. Like this "$MY_PATH/$MY_FOLDER". That should be done in case you forget to include / character in your path variable. It does not hurt to have two slashes, that means that /home/////////user/// folder is exactly the same /home/user/ folder.
Sometimes it is tricky to get ~ working, so using $HOME is a bit safer and more readable anyway.
So here is your modified script:
#!/bin/bash
MY_PATH="$HOME/Dropbox/Web_Development/"
MY_FOLDER='Test'
mkdir -p "$MY_PATH/$MY_FOLDER"
The problem is that your script sets PATH to a single directory, and that single directory does not contain a program called mkdir.
Do not use PATH as the name of a variable (use it to list the directories to be searched for commands).
Do learn the list of standard environment variable names and those specific to the shell you use (e.g. bash shell variables). Or use a simple heuristic: reserved names are in upper-case, so use lower-case names for variables local to a script. (Most environment variables are in upper-case — standard or not standard.)
And you can simply ensure that the directory exists by using:
mkdir -p ~/Dropbox/Web_Development
If it already exists, no harm is done. If it does not exist, it is created, and any other directories needed on the path to the directory (eg ~/Dropbox) is also created if that is missing.
This question already has answers here:
Why can't I change directories using "cd" in a script?
(33 answers)
Closed 4 years ago.
I 'm wondering of any mechanism that one could use to change the directory of a parent shell from sub-shell. For ex., I 'm running the script "settings.sh" in my $HOME. My $HOME has a directory $HOME/TEST/run. If my "settings.sh" scripts is as below
#!/bin/bash
# some shell related code
# some shell related code
cd $HOME/TEST/run
exit 0
I execute the above script at command prompt at $HOME. After the execution, I expect my command prompt in directory $HOME/TEST/run. I do understand that in sub-shell, it is being cd'd to $HOME/TEST/run, but at the end of the execution, it's back in $HOME.
Is there any elegant way of doing the above, using a single script. One way is to modify the script "settings.sh" to generate another script and then use ". $HOME/generatedScript.sh"
Nope, you can't. That's by design. Parent processes should never be affected by the results of a child without them wanting to be affected (otherwise sub-shells could do all sorts of nasty tricky things to the parent).
What you can do is have the shell save the information into a file or print the directory or ... Such that the parent at least can use it to change directories if the parent wants to.
Wes Hardaker explained the reasoning behind why executing that script does not cause it to change the directory of the parent shell. In order to work around that type of issue, you must "souce" the script instead of execute it. This causes the commands in the script to run in the current shell, rather than a child process.
. ./settings.sh
The first "." is a command which tells the shell to "source" the specified file. Here is the documentation from help .:
.: . filename [arguments]
Execute commands from a file in the current shell.
Read and execute commands from FILENAME in the current shell. The
entries in $PATH are used to find the directory containing FILENAME.
If any ARGUMENTS are supplied, they become the positional parameters
when FILENAME is executed.
Exit Status:
Returns the status of the last command executed in FILENAME; fails if
FILENAME cannot be read.