I'am reading in a variable that will contain a version of a certain file (Ex.: V1.0.10) by the following command.
read Version
and there is a possibility that that variable contains dots and I remove them by the next command:
New_Version=`echo $Version | sed -e 's/\.//g'`
but if I use this variable later on in the script, nothing changes at this variable, and I just use the cd command:
cd /data/group/$New_Version
or
cd /data/group/"$New_Version"
Then the error: No such file or directory... : line ...: cd:/data/group/V1010.
I double checked, the files exists, the name is correct but he doesn't find or recognize the directory?
What am I doing wrong?
Hope someone can help!
Thanks
UPDATE: The OP says that the problem was a hidden character in his input. This answer does not describe how to solve that problem. Nonetheless, the OP has marked this answer as accepted. See the comments of Charles Duffy for the actual solution to the OP's problem.
Caveat: I am taking everything in your problem description literally, which leads to the answer below. If you provide examples of the strings that will be passed through $Version it would help clarify the issue.
As I understand it you're reading in the full path of a file in your variable with read Version. Now if you say echo $Version you should get /path/to/foo.bar.
I don't think you'd want to cd into the file /path/to/foo.bar. You'll get an error: Not a directory, because it's a file, not a directory.
Now, consider what sed -e /\.//g will do to the pathname.
echo "/path/to/foo.bar" | sed -e '/\.//g'
/path/to/foobar
Does /path/to/foobar actually exist? No, because foo.bar was a file. You'll get an error: No such file or directory, because the foobar directory does not exist.
If I understand what you are trying to do, you are trying to extract the directory that contains the file specified by $Version. The command dirname /path/to/foo.bar will return /path/to. So you want to set New_version=$( dirname "$Version" ), at which point you should be able to cd $New_version.
P.S. Make sure $Version is reading in an absolute path name, not a relative name, so that it's independent of where you run the script from.
Related
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.
I run the following command to get the location of a .sh file and store it in a variable:
startupfile=`find . -name "startup.sh"`
The result of the above is:
./server1/bin/startup.sh
The next step is to run the startup.sh file.
When I try sh $startupfile, it fails to run the server.
Is there a way to ignore the ./ in the location value? Any other way to run the startup.sh script.
You should use:
startupfile=$(find . -name "startup.sh")
OR less preferred in BASH:
startupfile=`find . -name "startup.sh"`
This is called command substitution
UPDATE:: Post question edit:
To avoid running the script with full path you can place path of startup.sh in your PATH variable, so something like this in your ~/.bashrc:
export PATH=$PATH:~/server1/binripts
Then you can run:
startup.sh
From anywhere your home directory.
The ./ is harmless.
You have another problem. Did you change directory between the two commands? Is the "script" not actually a valid script?
The ./ is not the problem, it is simply a (superfluous) path to the current directory: ./file and ././././file are both valid paths to file in the current directory, the number of leading ./'s doesn't make any difference.
Looking at the source of your original question (before it was edited for better formatting), there were single quotes ' around the command: 'sh $startupfile'. If you had those in the actual command as well, that is your problem; the command should be: sh "$startupfile".
Other than that, it's hard to tell what the problem might be. Check that echo "$startupfile" shows the correct path and only the correct path. If it does, does sh ./server1/bin/startup.sh work? If not, what is the error?
To answer your specific questions:
Is there a way to ignore the ./ in the location value?
There are a couple of ways of stripping off leading directory names. The most efficient, though most obscure is:
startupfile=${startupfile##*/}
That removes the longest string on the left ending in '/'. The second way is a little more obvious, but involves creating a child process:
startupfile=$(basename "$startupfile")
Any other way to run the startup.sh script.
Yes, you could use the PATH environment variable, as #anubhava said. The only thing I would add is that you need execute access on the script; chmod u+x "$startupfile".
However, the implication of the name "startupfile" is that it is doing some sort of initialisation, in which case you might need to run it in the same process using the source or .(dot) command:
source "$startupfile"
You might still need the path in the filename, or update PATH.
I want to write a cript for a user to set an installation path.
I am using this
read $file_path
cd $file_path
But it does not change to the path saved on that variable.
How can i set this exactly because this seems the wrong way?
read does not use the $ to read the variable. Hence, it should be
read file_path
cd $file_path
Somewhat reading between the lines, I think you are trying to call a script which you expect to change directory of the caller: For example:
myscript:
read file_path
cd "$file_path"
command-line:
./myscript
and you find it hasn't changed the directory. That's because you are running the script in a child process. It changes the current directory of the child, then returns to the parent, which is unaffected.
You need to source the file instead, that is, run the commands in the current process, not a separate one:
. ./myscript
Yes, notice the extra 'dot' . at the start. This is generically known as the source command, and you can use source instead of 'dot' on bash and csh.
I am a newbie to Shell scripting. I want to delete all the contents of a directory which is in HOME directory of the user and deleting some files which are matching with my conditions. After googled for some time, i have created the following script.
#!/bin/bash
#!/sbin/fuser
PATH="$HOME/di"
echo "$PATH";
if [ -d $PATH ]
then
rm -r $PATH/*
fuser -kavf $PATH/.n*
rm -rf $PATH/.store
echo 'File deleted successfully :)'
fi
If I run the script, i am getting error as follows,
/users/dinesh/di
dinesh: line 11: rm: command not found
dinesh: line 12: fuser: command not found
dinesh: line 13: rm: command not found
File deleted successfully :)
Can anybody help me with this?
Thanks in advance.
You are modifying PATH variable, which is used by the OS defines the path to find the utilities (so that you can invoke it without having to type the full path to the binary). The system cannot find rm and fuser in the folders currently specified by PATH (since you overwritten it with the directory to be deleted), so it prints the error.
tl;dr DO NOT use PATH as your own variable name.
PATH is a special variable that controls where the system looks for command executables (like rm, fuser, etc). When you set it to /users/dinesh/di, it then looks there for all subsequent commands, and (of course) can't find them. Solution: use a different variable name. Actually, I'd recommend using lowercase variables in shell scripts -- there are a number of uppercase reserved variable names, and if you try to use any of them you're going to have trouble. Sticking to lowercase is an easy way to avoid this.
BTW, in general it's best to enclose variables in double-quotes whenever you use them, to avoid trouble with some parsing the shell does after replacing them. For example, use [ -d "$path" ] instead of [ -d $path ]. $path/* is a bit more complicated, since the * won't work inside quotes. Solution: rm -r "$path"/*.
Random other notes: the #!/sbin/fuser line isn't doing anything. Only the first line of the script can act as a shebang. Also, don't bother putting ; at the end of lines in shell scripts.
#!/bin/bash
path="$HOME/di"
echo "$path"
if [ -d "$path" ]
then
rm -r "$path"/*
fuser -kavf "$path"/.n*
rm -rf "$path/.store"
echo 'File deleted successfully :)'
fi
This line:
PATH="$HOME/di"
removes all the standard directories from your PATH (so commands such as rm that are normally found in /bin or /usr/bin are 'missing'). You should write:
PATH="$HOME/di:$PATH"
This keeps what was already in $PATH, but puts $HOME/di ahead of that. It means that if you have a custom command in that directory, it will be invoked instead of the standard one in /usr/bin or wherever.
If your intention is to remove the directory $HOME/di, then you should not be using $PATH as your variable. You could use $path; variable names are case sensitive. Or you could use $dir or any of a myriad other names. You do need to be aware of the key environment variables and avoid clobbering or misusing them. Of the key environment variables, $PATH is one of the most key ($HOME is another; actually, after those two, most of the rest are relatively less important). Conventionally, upper case names are reserved for environment variables; use lower case names for local variables in a script.
I'm trying to find what the unix equivalent of the Windows/DOS variable %cd% is. What I'm looking for is an environmental variable or a workaround that will let me set a variable to the path of the file currently running.
For example, if the program is in /home/chris/Desktop but the working directory is /home/chris, what would be the command to get ~/Desktop as opposed to pwd which will give me /home/chris.
In BASH, you can look at the $PWDvariable. That'll show your Present Working Directory. Getting the relationship between the $PWD and where the program is located is a bit trickier. You can look at the $0 variable which should give you the name of the file I ran the following script:
#! /bin/bash
#
echo "PWD = $PWD"
echo "\$0 = $0"
And got the following result:
$ test.sh
PWD = /Users/david
$0 = /Users/david/bin/test.sh
The $0 gives you the name of the file from the root of the OS. Taking the dirname will give you the file name. Somehow, if you can filter out the PWD from the $0, you might get what you're looking for. I had some luck with the following:
curPath=$(dirname "${0#$PWD/}")
Didn't thoroughly test it, from what I can see, it seems to do what you want. What it can't do is do something like this:
$ test.sh
PWD = /Users/david/someSubDir
$0 = /Users/david/bin/test.sh
The current path is /Users/david/bin/test.sh
It would have been nice if it could do this:
The current path is ../bin/test.sh
Although the former is correct.
The readlink command doesn't work on non-Linux systems.
How about dirname $(readlink -f $0)
readlink -f $0 returns the canonicalized path to the running script.
dirname removes everything after and including the final \.
This way works, but isn't 100% reliable:
${0%/*}
The way that works is that it reads $0 (the program name), and strips off everything from the final slash onwards. It's not reliable because if your script is invoked via a symlink, you will get the directory containing the symlink, not the directory containing the real script.
Also, it's possible to pass in a "fake" value for $0, for example by using exec -a. So even if you aren't using symlinks, it's still not a 100% solution. In fact, such a solution doesn't exist.
Working with what Chris suggested, you could use the which command. According to the man page, which reports the full path of the executable that would have been executed if its argument had been entered at the shell prompt. Since we know $0 was entered at the shell prompt, we can use `which $0` to report exactly the path that was used to execute. Unfortunately, this still suffers from the symlink issue, as which does not provide options to avoid symlinks.