I'm trying to store all my profile configuration files (~/.xxx) in git. I'm pretty horrible at bash scripting but I imagine this will be pretty straight forward for you scripting gurus.
Basically, I'd like a script that will create symbolic links in my home directory to files in my repo. Twist is, I'd like it warn and prompt for overwrite if the symlink will be overwriting an actual file. It should also prompt if a sym link is going to be overwritten, but the target path is different.
I don't mind manually editing the script for each link I want to create. I'm more concerned with being able to quickly deploy new config scripts by running this script stored in my repo.
Any ideas?
The ln command is already conservative about erasing, so maybe the KISS approach is good enough for you:
ln -s git-stuff/home/.[!.]* .
If a file or link already exists, you'll get an error message and this link will be skipped.
If you want the files to have a different name in your repository, pass the -n option to ln so that it doesn't accidentally create a symlink in an existing subdirectory of that name:
ln -sn git-stuff/home/profile .profile
...
If you also want to have links in subdirectories of your home directory, cp -as reproduces the directory structure but creates symbolic links for regular files. With the -i option, it prompts if a target already exists.
cp -i -as git-stuff/home/.[!.]* .
(My answer assumes GNU ln and GNU cp, such as you'd find on Linux (and Cygwin) but usually not on other unices.)
The following has race conditions, but it is probably as safe as you can get without filesystem transactions:
# create a symlink at $dest pointing to $source
# not well tested
set -e # abort on errors
if [[ ( -h $dest && $(readlink -n "$dest") != $source ) || -f $dest || -d $dest ]]
then
read -p "Overwrite $dest? " answer
else
answer=y
fi
[[ $answer == y ]] && ln -s -n -f -v -- "$source" "$dest"
Related
I wrote a simple bash script (in /homedir) to run an executable and then move the outputs to /workdir. I also made a soft link of /workdir named work to /homedir for me to switch easily between folders.
All steps are working well, except that an unspecified soft link named 'grids' is created in /workdir to itself. I can't delete it otherwise all outputs are gone as well.
How can this happen?
#!/bin/bash
cd ..
expname=`basename "$PWD"`
echo 'experiment name: '$expname
homedir=/home/b/b380963/icon_foehn/$expname/grids/
workdir=/work/bb1096/b380963/icon_foehn/$expname/grids/
if [ ! -d ${workdir} ]; then
mkdir -p ${workdir}
fi
cd $homedir
ln -s ${workdir} work
cd /home/b/b380963/nwp/dwd_icon_tools_v2/icontools/
./icongridgen --nml $homedir/gridgen_MCH_july.nml
mv ICON_1E_* $workdir/
mv base_grid* $workdir/
It's quite easy to see in your code:
workdir=/work/bb1096/b380963/icon_foehn/$expname/grids/
...
ln -s ${workdir} work
The command ln -s is the command, creating the symlink.
If you don't like the creation of that symlink, you might put that line in comment (don't delete it: in case you're not satisfied, it's easier to uncomment it).
You can solve your issue, using this command:
ln -sTf ...
This removes the existing destination files beforehand.
The script is located here: https://github.com/docker-library/ghost/blob/master/docker-entrypoint.sh
#!/bin/bash
set -e
if [[ "$*" == npm*start* ]]; then
baseDir="$GHOST_SOURCE/content"
for dir in "$baseDir"/*/ "$baseDir"/themes/*/; do
targetDir="$GHOST_CONTENT/${dir#$baseDir/}"
mkdir -p "$targetDir"
if [ -z "$(ls -A "$targetDir")" ]; then
tar -c --one-file-system -C "$dir" . | tar xC "$targetDir"
fi
done
if [ ! -e "$GHOST_CONTENT/config.js" ]; then
sed -r '
s/127\.0\.0\.1/0.0.0.0/g;
s!path.join\(__dirname, (.)/content!path.join(process.env.GHOST_CONTENT, \1!g;
' "$GHOST_SOURCE/config.example.js" > "$GHOST_CONTENT/config.js"
fi
ln -sf "$GHOST_CONTENT/config.js" "$GHOST_SOURCE/config.js"
chown -R user "$GHOST_CONTENT"
set -- gosu user "$#"
fi
exec "$#"
From what I know, it says that if you use some variation of npm start to move some files around from $GHOST_SOURCE to $GHOST_CONTENT, do something to the config.js file, link the config file, set ownership of the content files, and then execute npm start as the user user. Otherwise, it just runs your commands normally.
The specifics are what are hard for me to understand because there are a lot of things from bash that I've never seen before. So I have a lot of questions.
for dir in "$baseDir"/*/ "$baseDir"/themes/*/; do
In the above, why do they specify both /*/ and /themes/*/? Shouldn't /*/ contain themes? Is * not a wildcard for some reason?
targetDir="$GHOST_CONTENT/${dir#$baseDir/}"
In the above, what is the point of # in the variable expansion?
tar -c --one-file-system -C "$dir" . | tar xC "$targetDir"
In the above, does this somehow save time? Why not use something like rsync? I understand the point of -C, but why -c and --one-file-system?
sed -r '
s/127\.0\.0\.1/0.0.0.0/g;
s!path.join\(__dirname, (.)/content!path.join(process.env.GHOST_CONTENT, \1!g;
' "$GHOST_SOURCE/config.example.js" > "$GHOST_CONTENT/config.js"
What does this sed command do? I know it's a replacement, but why the "$GHOST_SOURCE/config.example.js" > "$GHOST_CONTENT/config.js" as the end?
ln -sf "$GHOST_CONTENT/config.js" "$GHOST_SOURCE/config.js"
In the above, what is the point of this symlink? Why try to link them to each other if both files already exist?
set -- gosu user "$#"
In the above what does calling set with no args do?
I hope that's not too much. I felt making a separate question for each of these would be too much especially since it's all related to each other.
for dir in "$baseDir"/*/ "$baseDir"/themes/*/; do
In the above, why do they specify both /*/ and /themes/*/? Shouldn't
/*/ contain themes? Is * not a wildcard for some reason?
themes/ is in the first match, but themes/*/ is not, so you need the second entry to include the contents of themes.
targetDir="$GHOST_CONTENT/${dir#$baseDir/}"
In the above, what is the point of # in the variable expansion?
It removes the $baseDir prefix from $dir. So for example:
bash$ dir=/home/bmitch/data/docker
bash$ echo $dir
/home/bmitch/data/docker
bash$ echo ${dir#/home/bmitch}
/data/docker
tar -c --one-file-system -C "$dir" . | tar xC "$targetDir"
In the above, does this somehow save time? Why not use something like
rsync? I understand the point of -C, but why -c and --one-file-system?
rsync may not be installed on every machine by default, tar is fairly universal. The -c is to create, vs extract, and --one-file-system avoids tar continuing to an outside mount point (nfs, symlink to root, etc).
sed -r '
s/127\.0\.0\.1/0.0.0.0/g;
s!path.join\(__dirname, (.)/content!path.join(process.env.GHOST_CONTENT, \1!g;
' "$GHOST_SOURCE/config.example.js" > "$GHOST_CONTENT/config.js"
What does this sed command do? I know it's a replacement, but why the
"$GHOST_SOURCE/config.example.js" > "$GHOST_CONTENT/config.js" as the
end?
config.example.js is the input (last arg to the sed), config.js is the output (after the >). So it takes the config.example.js, change the ip address from 127.0.0.1 to 0.0.0.0, effectively listening on all interfaces/ip's instead of just internally on the loopback. The second half of the sed is changing the path.join arguments from __dirname to process.env.GHOST_CONTENT.
ln -sf "$GHOST_CONTENT/config.js" "$GHOST_SOURCE/config.js"
In the above, what is the point of this symlink? Why try to link them
to each other if both files already exist?
The $GHOST_SOURCE/config.js is replaced (-f) with a link to $GHOST_CONTENT/config.js. Symbolic links give a file name reference to another actual file, so there will be two names, but one copy of the data, which means you will only have a single configuration in this situation.
set -- gosu user "$#"
In the above what does calling set with no args do?
This changes the values of $1, $2, ... $n to be $1=gosu, $2=user, $3=the old $1, $4=the old $2..., essentially adding the gosu and user to the beginning of the passed parameters to the script. The -- makes sure that set doesn't interpret any values from $# as a flag for itself.
Here is the condition:
I have a file with all packages installed.
I have a folder with all kinds of other packages, but they include all of the ones in the list, plus more.
I need a bash script that will read the file and check a folder for packages that don't exist in the list then remove them, they are not needed, but keep the packages that are on the list in that folder.
Or perhaps the bash should read folder then if packages in the folder aren't on the list them rm -f that or those packages.
I am familiar with writing if then conditional statements, I just don't know how to do if making the items in the list a variable or variables (in a loop).
thanks!
I would move the packages on the list to a new folder, delete the original folder, and move the temporary folder back:
DIR=directory-name
mkdir "$DIR-tmp"
while read pkgname; do
if [[ -f "$DIR/$pkgname" ]]; then
mv "$DIR/$pkgname" "$DIR-tmp"
fi
done < package-list.txt
# Confirm $DIR-tmp has the files you want first!
rm -rf "$DIR"
mv "$DIR-tmp" "$DIR"
I think you want something like this:
for file in $(ls folder) ; do
grep -E "$file" install-list-file >/dev/null || \
echo $file
done > rm-list
vi rm-list # view file to ensure correct
rm $(<rm_list)
There are ways to make this faster (using parameter substitution to avoid fork/exec's), but I recommend avoiding fancy shell stuff [${file##*/}] until you've got the basics down. Also, this script basically translates the description into a script and is not intended to be much more than a guide on how to approach the problem.
I need to write a script that will recreate my opt folder if it gets deleted when I remove a package from it. Here's a link to my previous post: dpkg remove to stop processes
Now, the issue I'm running into could be better described here: http://lists.debian.org/debian-devel/2006/03/msg00242.html
I was thinking of just adding a postrem script which checks if an opt directory exists, and if not, creates one. My experience with shell scripts is pretty limited though..
[ -d "$dir" ] || mkdir -p "$dir"
This could be written more verbosely / clearly as:
if ! test -d "$dir"; then
mkdir -p "$dir"
fi
See help test for more information.
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.