I have adapted a shell script to symlink my dotfiles from a git repository into my home directory. It works well except for one issue... it seems to be creating a symlink of a directory inside the directory itself.
In the below script, the .vscode entry in the files variable is a directory. When I run the script initially (when no symlinks are created) everything works fine. When I run the script a second time it still works fine. However, on the third run, a .vscode symlink is created WITHIN the ~/projects/dotfiles/.vscode folder.
#!/bin/bash
############################
# makesymlinks.sh
# This script creates symlinks from the home directory to any desired dotfiles in ~/projects/dotfiles
############################
########## Variables
dir=~/projects/dotfiles # dotfiles directory
olddir=~/dotfiles_old # old dotfiles backup directory
files=(.vscode .bash_profile .zshrc .gitconfig) # list of files/folders to symlink in homedir
##########
# create dotfiles_old in homedir
echo -n "Creating $olddir for backup of any existing dotfiles in ~ ..."
mkdir -p $olddir
echo "done"
# change to the dotfiles directory
echo -n "Changing to the $dir directory ..."
cd $dir
echo "done"
# move any existing dotfiles in homedir to dotfiles_old directory, then create symlinks from the homedir to any files in the ~/projects/dotfiles directory specified in $files
for file in $files; do
echo "Moving any existing dotfiles from ~ to $olddir"
mv ~/$file $olddir/
echo "Creating symlink to $file in home directory."
ln -s $dir/$file ~/$file
done
I would like to prevent this from happening, but I cannot see why it is. How can I prevent this?
From the man page of ln(1):
-n If the target_file or target_dir is a symbolic link, do not follow
it. This is most useful with the -f option, to replace a symlink
which may point to a directory.
Try updating your ln -s command to use ln -sfn instead.
Related
I am trying to make a bash script to create directories with the same name as each file in a given directory, then move said files to their respective directories, and then rename the files.
Basically - a quantum chemistry program that I use requires that the input files be named "ZMAT". So, if I have multiple jobs, I currently need to manually create directories, and then move the ZMAT files into them (can only run one job per folder).
When I run my code, I get "binary operator expected". I am not sure what this means. Some help please.
Here is what I have so far:
#!/bin/bash
if [ -e *.ZMAT ];
then
echo "CFOUR Job Detected"
for INPFILE in *.ZMAT; do
BASENAME=$(basename $INPFILE )
INPFILE=$BASENAME.ZMAT
OUTFILE=$BASENAME.out
XYZFILE=$BASENAME.xyz
ERRORFILE=$BASENAME.slu
if [ ! -e $ERRORFILE ];
then
# Create folder in scratch directory with the basename
mkdir /scratch/CFOUR/$BASENAME
# Move the file to its directory
mv -f $INPFILE /scratch/CFOUR/$BASENAME
# cd to the new directory
cd /scratch/CFOUR/$BASENAME
# Change the file name to just ZMAT
mv -f $INPFILE ZMAT
echo "Submitting CFOUR Job"
# Submit to scheduler
#RUN_COMMAND="sbatch -J $BASENAME _CFOUR_MRCC_SLURM.SUB"
#eval $RUN_COMMAND
else
echo "Error File Detected - Not Submitting Job"
fi
done
fi
An alternative would be to create symlinks to the original files.
As you said before, each ZMAT symlink would need to be in its own directory.
The upside is that the original data doesn't move, so less risk of breaking it, but the tool you want to use should read the symlinks as if they are the files it is looking for.
This one-liner creates an out directory in the current folder that you could subsequently move wherever you want it. You could easily create it where you do want it by replacing "out" with whatever absolute path you wanted
for i in *.ZMAT; do mkdir -p out/$i ; ln -s $PWD/$i out/$i/ZMAT ; done
I believe I have solved my problem. Here is the new script, which appears to be working fine. Any input is welcome though!
#!/bin/bash
SUBDIR=$(pwd)
for i in *.ZMAT; do
BASENAME=$(basename $i .ZMAT)
INPFILE=$BASENAME.ZMAT
OUTFILE=$BASENAME.out
XYZFILE=$BASENAME.xyz
ERRORFILE=$BASENAME.slu
if [ ! -e $ERRORFILE ];
then
mkdir /scratch/CFOUR/$BASENAME # Create Scratch Folder
cp $INPFILE /scratch/cdc/CFOUR/$BASENAME # Move Input to Scratch
cd /scratch/CFOUR/$BASENAME #cd to Scratch Folder
mv -f $INPFILE ZMAT # Change Input Name
echo "Submitting CFOUR Job"
# Submit to scheduler
#RUN_COMMAND="sbatch -J $BASENAME _CFOUR_MRCC_SLURM.SUB"
#eval $RUN_COMMAND
cd $SUBDIR #Go back to SUBDIR
else
echo "Error File Already Exists"
fi
done
I ran this script to set up some dotfiles managment:
#!/bin/bash
############################
# .make.sh
# This script creates symlinks from the home directory to any desired dotfiles in ~/dotfiles
############################
########## Variables
dir=~/dotfiles # dotfiles directory
olddir=~/dotfiles_old # old dotfiles backup directory
files="bashrc vimrc vim zshrc oh-my-zsh private scrotwm.conf Xresources" # list of files/folders to symlink in homedir
##########
# create dotfiles_old in homedir
echo -n "Creating $olddir for backup of any existing dotfiles in ~ ..."
mkdir -p $olddir
echo "done"
# change to the dotfiles directory
echo -n "Changing to the $dir directory ..."
cd $dir
echo "done"
# move any existing dotfiles in homedir to dotfiles_old directory, then create symlinks from the homedir to any files in the ~/dotfiles directory specified in $files
for file in $files; do
echo "Moving any existing dotfiles from ~ to $olddir"
mv ~/.$file ~/dotfiles_old/
echo "Creating symlink to $file in home directory."
ln -s $dir/$file ~/.$file
done
install_zsh () {
# Test to see if zshell is installed. If it is:
if [ -f /bin/zsh -o -f /usr/bin/zsh ]; then
# Clone my oh-my-zsh repository from GitHub only if it isn't already present
if [[ ! -d $dir/oh-my-zsh/ ]]; then
git clone http://github.com/michaeljsmalley/oh-my-zsh.git
fi
# Set the default shell to zsh if it isn't currently set to zsh
if [[ ! $(echo $SHELL) == $(which zsh) ]]; then
chsh -s $(which zsh)
fi
else
# If zsh isn't installed, get the platform of the current machine
platform=$(uname);
# If the platform is Linux, try an apt-get to install zsh and then recurse
if [[ $platform == 'Linux' ]]; then
sudo apt-get install zsh
install_zsh
# If the platform is OS X, tell the user to install zsh :)
elif [[ $platform == 'Darwin' ]]; then
echo "Please install zsh, then re-run this script!"
exit
fi
fi
}
install_zsh
However it's changed my $ sign on the bash terminal to a % and changed a few other things to.
I was wondering how do I undo this script and revert my bash back to the $ sign, I can't see how to revert everything.
It installed zsh as your default shell instead of bash. Run
chsh -s $(which bash)
(read as «change shell to this shell: which file is bash»)
I have following directory structure that I pull from github;
dotfiles/bash
.bashrc
.bash_profile
.some_other
env
dotfiles/tmux/
.tmux.conf
dotfiles/???/
.whatever
bash/In my bashcode (below) I'd like to (in my script symlink.sh) to iterate trough the subfolders and symlink certain, not all, dotfiles to my ~home dir. How can I do that dynamically? Since I don't know how many subfolders there are, and what files in those subfolders that I wan't to symlink this has to be done dynamically.
My code below
!/bin/bash
############################
# .make.sh
# This script creates symlinks from the home directory to any desired dotfiles in ~/dotfiles
############################
set -x
trap read debug
########## Variables
dir=~/dotfiles # dotfiles directory
olddir=~/dotfiles_old # old dotfiles backup directory
files=".bashrc .bash_profile env .tmux.conf" # list of files/folders to symlink in homedir
##########
# create dotfiles_old in homedir
echo "Creating $olddir for backup of any existing dotfiles in ~"
mkdir -p $olddir
echo "...done"
# change to the dotfiles directory
echo "Changing to the $dir directory"
cd $dir
echo "...done"
paus -p ;clear
# move any existing dotfiles in homedir to dotfiles_old directory, then create symlinks
for file in $dir/*/*; do
echo $dir;ls -a
if [[ -f $dir/${files##*/} && ! -L $dir/${files##*/} ]]; then
$olddir=$(mktemp -d olddotfiles.XXXXXX)
#mv "~/${files##*/}" "$backupdir"
ls -a
ln -s "$files" ~/${files##*/}
fi
done
Just use the find command - change this:
for file in $dir/*/*; do
To this:
for file in $(find $dir); do
I found this one for my dotfiles. But I don't understand why the author makes dotfiles_old. Instead you just move to dotfiles.
Is there any good reasons to do it so? If so why?
Can I do it without dofiles_old? Is is a good idea?
Code:
#!/bin/bash
############################
# .make.sh
# This script creates symlinks from the home directory to any desired dotfiles in ~/dotfiles
############################
########## Variables
dir=~/dotfiles # dotfiles directory
olddir=~/dotfiles_old # old dotfiles backup directory
files="bashrc vimrc vim zshrc oh-my-zsh" # list of files/folders to symlink in homedir
##########
# create dotfiles_old in homedir
echo "Creating $olddir for backup of any existing dotfiles in ~"
mkdir -p $olddir
echo "...done"
# change to the dotfiles directory
echo "Changing to the $dir directory"
cd $dir
echo "...done"
# move any existing dotfiles in homedir to dotfiles_old directory, then create symlinks
for file in $files; do
echo "Moving any existing dotfiles from ~ to $olddir"
mv ~/.$file ~/dotfiles_old/
echo "Creating symlink to $file in home directory."
ln -s $dir/$file ~/.$file
done
With ~/dotfiles_old, if you discover that you forgot to update ~/dotfiles/bashrc with your new fancy prompt, you can just copy it from ~/dotfiles_old/.bashrc.
Without ~/dotfiles_old, any changes you've made in your home directory will be permanently lost.
Whether you care is entirely up to you. It's for the benefit of the user, and not the script.
the best approach is to create a folder dotfiles in $ HOME and make symlinks to their configuration files.
Have an example here: https://github.com/vsouza/dotfiles
Each folder has a install.sh file that takes care of creating symbolic links.
I want to prompt the user for a directory name, and have them able to tab-complete to a valid directory.
So far, I've got tab-completion working for both files and directories using "read -e". I only want directories to be autocompleted when the user presses tab.
Here's what I have:
echo "Enter a directory"
read -e -p "> " DEST
How can I make bash only return a list of directories when the user presses tab, rather than a list of files and directories?
An alternate approach that gives you a lot of flexibility is to use compgen; see my answer here for details.
Here's my quick take at the problem. For some reason I had to actually use bash and not sh on my computer, due to the use of pushd and popd. I think it's well commented enough for me to not explain it any further.
#!/bin/sh
tempdir=`mktemp -d`
# save the current directory
pushd .
# make a new folder, then make a bunch of new directories
# mirroring those in our current directory
for i in $(find . -type d); do mkdir "$tempdir/$i" ; done
# change to the temporary directory
cd "$tempdir"
echo "Enter a directory"
read -e -p ">" DEST
echo "You told me $DEST"
# return to our original directory
popd
# clear out that temporary directory we made
rm -rf "$tempdir"
But Jacob's response is probably more efficient and cleaner than mine.