I am writing a script that will symlink all of my dotfiles related to bash into the home directory. I want the script to check if the filenames already exists, so that I I can rename/move them.
For some reason can't get my if test-command to recognize filenames that have an underscore in them.
When testing for files that already exist, this script:
#!/bin/bash
for name in bashrc bash_profile bash_aliases
do
filename=$HOME"/."$name
if [ -e "$filename" ]; then
echo "${filename} exists"
else
echo "${filename} doesn't exist"
fi
done
Outputs:
/home/xavier/.bashrc exists
/home/xavier/.bash_profile doesn't exist
/home/xavier/.bash_aliases doesn't exist
What is it about the underscore that is causing this behavior, and how do I fix it?
The code is correct as posted and underscore is not a problematic character in general.
You mention that you're symlinking the files -- if you're sure the files are there, verify that they are not broken symlinks. -e file will be false if the final target of the link doesn't exist.
Other things that can cause this are:
lacking permissions
invisible unicode characters like a zero-width space
similar-looking unicode characters like bаsh_profile which has a fullwidth low line instead of an underscore.
running the script in a chroot or sandbox
checking that the file exists in a different terminal than the one used for running the script -- it could be chrooted, SSH'd to another machine or started before a directory was mounted over the dir, and therefore have a different view of the fs
Related
I need help trying to find a solution to have a bash script be able to read file names with special characters. The user will start the script, but if the folder or the file has special characters, the script will fail or have an error. I have tried several options I found online, but I have not been able to make them work with the script.
The script is set up to take user input with the read command.
read -r -p "Enter directory name : " var1
If the user input is “accoutn&orders,” the script will fail due to the ‘&’ character as it won’t find the directory or file.
When the script looks for the file with specific extensions, the input folder name will be the path to copy the files to a different directory. The issue I am running into is that some of those files or directories have special characters, and the script cannot process the variables and cannot find the file when there are special characters.
The script uses a for loop to check every file in the directory, and if the file's name has a special character, it will fail the loop.
example file name:
file1#depot.rct
file2&logrecord.rct
cd $var1
ls: cannot access '/sharepool/comunityshare//'\''account.&.orders'\''': No such file or directory
line 141: cd: '/sharepool/comunityshare//'\''account.&.orders'\''': No such file or directory
I have tried using single quotes wrapping and bask slashes, but the variable is not readable.
Please note that I am not a coder or developer, I know some basic Linux commands, and I am trying to make this work while a better process is developed. I appreciate your help with this.
I was able to solve the issue using this line.
filename=$(echo "$filename" | sed 's/[&()#+*#!%^'\''^]/\\&/g')
That inserted a backslash if the variable had a special character.
account.&.orders to account.&.orders
Thank you for your help and support.
I have some config files saved to my google drive and I want to source them in my bash init files (I use .bashrc).
The most recent version of Google drive prefaces your drive with your gmail address and spaces. As in "my.name#gmail.com - Google Drive/ My Drive". I'd like to save this address in a variable that I could use downstream in .bashrc. Extra points if I can cd $GDRIVE in the interactive shell as well, but I really care about the config files.
The relevant lines in my file look like this
GDRIVE1="~/myname\#gmail.com\ -\ Google\ Drive/My\ Drive"
GDRIVE2="\"~/myname#gmail.com - Google Drive/My Drive\""
GDRIVE=$GDRIVE2
# Import alias definitions.
if [ -f "$GDRIVE"/Sysadmin/ConfigFiles/.bash_aliases ]; then
. ~/Google\ Drive/Sysadmin/ConfigFiles/.bash_aliases
else
echo "Could not find bash alias file."
fi
GDRIVE1 and GDRIVE2 are just there to illustrate things I've tried.
A tilde only expands to your home directory if it's unquoted. It needs to be outside of double quotes. Meanwhile you can leave the spaces unescaped as long as they are quoted.
GDRIVE=~/"myname#gmail.com - Google Drive/My Drive"
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.
For some strange reason, I'm getting a "No such file or directory" error for my $PATH variable. I have tried to edit my path using export, changing it from what it was originally to every permutation from a single directory path to the original.
When there is one directory (e.g., export PATH=/bin), I get "/bin: Is a Directory". But once I add more than one directory (e.g., export PATH=/bin:/sbin), I get "No such file or directory".
I'm curious to see what the cause of this issue is!
RE; your comment:
/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin:/usr/local/git/bin:/usr/local/mysql/bin: No such file or directory will be generated if you have a line which says:
$PATH
maybe on its own, or maybe you have $PATH=.... That is, the shell is trying to execute a program named:
/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin:/usr/local/git/bin:/usr/local/mysql/bin
Lose the $ on the left-hand side.
I'm not sure you are using the export variant. You almost certainly have spaces in there and you shouldn't, as per the following transcript:
pax> PATH= /bin
bash: /bin: is a directory
pax> PATH= /bin/sbin
bash: /bin/sbin: No such file or directory
The first is caused because you're setting the path temporarily to an empty string while attempting to run that directory. That's because you can do things like:
pax> xyzzy=1
pax> echo $xyzzy
1
pax> xyzzy=2 bash -c 'echo $xyzzy'
2
pax> echo $xyzzy
1
In other words, it's a way of changing an environment variable for a single command, and having it automatically revert when the command is finished.
The second case is simply because there is no /bin/sbin directory. So it detects that before it complains about the fact that you're trying to run a directory.
Setting a variable in bash is a no-space thing (unless you have spaces in your directory names, in which case they should be quoted). In addition, they need to be colon-sparated. Hence you're looking for things like:
PATH=/bin
PATH=/bin:/sbin
PATH="/bin:/sbin:/directory with spaces in it:$HOME/bin"
The export function will only change the variable for the current terminal session.
Write your PATH inside ~/.bash_profile if you want to change it permanently.
For this modification to work you have to close your current terminal and reopen it.
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.