I have a Bash function named "inDir" which abstracts the "go to a directory, do something, and come back to the starting directory" pattern. It is defined as:
inDir() {
if [ $# -gt 1 ]; then
local dir="$1"
local cwd=`pwd`
shift
if [ -d "$dir" ]; then
cd "$dir" && "$#"
cd "$cwd"
fi
fi
}
I am trying to create a function whose semantics don't matter much, but will essentially run:
inDir /tmp { [ -e testFile ] && touch testFile }
I hope the "implied" semantics are clear. I want to go into a directory, check if $somefile exists, and if it does, delete it. This is not working as intended. If I run:
cd
inDir /tmp [ -e testFile ] && touch testFile
it checks if testFile exists in /tmp, and then tries to touch it in ~. Can anybody think of a good way to invoke inDir so that it accepts "compound" commands?
indir() {
if [ -d "$1" ]; then
local dir="$1"
shift
(cd "$dir" && eval "$#")
fi
}
indir /tmp touch testFile
indir /tmp "[ -e testFile ] && rm testFile"
Nope. Just tell it to invoke a subshell.
inDir /tmp bash -c "[ -e testFile ] && touch testFile"
Related
in my shell scripts I need to rotate log directories.
I am looking for a more compact, scaleable and elegant way than this, but have currently no idea how to solve this i.e. in a while loop and to calculate with variables.
function f_rotate_logdirs()
{
if [ -d $LOGDIR_OLD14 ]; then
# be extra cautious, no rm -rf operation ...
rm -rf $LOGDIR_OLD14
fi
if [ -d $LOGDIR_OLD13 ]; then
mv $LOGDIR_OLD13 $LOGDIR_OLD14
fi
[...]
if [ -d $LOGDIR_OLD1 ]; then
mv $LOGDIR_OLD1 $LOGDIR_OLD2
fi
if [ -d $LOGDIR ]; then
mv $LOGDIR $LOGDIR_OLD1
fi
mkdir -p $LOGDIR
echo $DATE > $LOGDIR/0.DATE
}
Do you have an idea for a more compact code which easily scales up to n_days ?
Any help on this would be much appreachiated.
Many thanks for this upfront.
The following works:
rm -r -f LOGDIR15
seq 1 14 | tac | xargs -t -n1 sh -c 'if [ -d "$1$2" ]; then mv -n "$1$2" "$1$(($2+1))"; fi' -- LOGDIR
if [ -d "LOGDIR" ]; then mv -n "LOGDIR" "LOGDIR1"; fi
mkdir LOGDIR
We need to handle the first and last separately. Making a function out of it, would be:
backup() {
rm -r -f "$1$2"
seq 1 "$(($2 - 1))" | tac |
xargs -t -n1 sh -c 'if [ -d "$1$2" ]; then mv -n "$1$2" "$1$(($2+1 ))"; fi' -- "$1"
if [ -d "$1" ]; then mv -n "$1" "$1"1; fi
mkdir "$1"
}
with usage:
backup LOGDIR 15
would move the directory named LOGDIR to LOGDIR1 and LOGDIR2 and .. LOGDIR15.
It seems unknown to me, why you use variables $LOGDIR_OLD15 and not just directory names themselves.
Thanks for your input, the commands seq and tac were new to me.
Now I found some time to code it newly and I took some of your nice ideas.
My aim was to get a scaleable solution which is easy to understand and maintain.
I decided to name the current logdir "$dir.0" because some of my scripts gather config diffs of devices that are being taking on daily basis. This makes coding a little bit easier for getting the diffs between i.e. "5 and 3 days ago" or between "2 days ago and today".
#! /bin/sh
# Customizeable settings
LOGDIR_BACKUPS=14 # max number of backups
# Directories
LOGDIR_NAME=logs
LOGDIR=$LOGDIR_NAME.0
# Defines
DATE=`date +'%Y%m%d-%H%M'`
function f_rotate_logdirs() {
local dir=$1 # name of logdir folder
local max=$2 # max #
local min=0 # $dir.0 = current logdir
for i in `seq $min $max | tac`
do
case $i in
$max) if [ -d $dir.$i ]; then
rm -f $dir.$i/*
rmdir $dir.$i
fi
;;
$min) if [ -d $dir.$i ]; then
mv $dir.$i $dir.$((i+1))
fi
mkdir $dir.$i
echo $DATE > $dir.$i/0.DATE
;;
*) if [ -d $dir.$i ]; then
mv $dir.$i $dir.$((i+1))
fi
;;
esac
done
}
f_rotate_logdirs $LOGDIR_NAME $LOGDIR_BACKUPS
I forked https://github.com/mathiasbynens/dotfiles dotfiles and tried to run bootstrap.sh which apparently should "pull in the latest version and copy the files to your home folder", according to the README.md
But when I try to source the bootstrap.sh, error returns. "bootstrap.sh:13 := not found". Line 13 is the doIt part in
if [ "$1" == "--force" -o "$1" == "-f" ]; then
doIt;
Does anybody have an idea where it went wrong? Thanks in advance.
#!/usr/bin/env bash
cd "$(dirname "${BASH_SOURCE}")";
git pull origin master;
function doIt() {
rsync --exclude ".git/" --exclude ".DS_Store" --exclude ".osx" \
--exclude "bootstrap.sh" --exclude "README.md" --exclude "LICENSE-MIT.txt" -avh --no-perms . ~;
source ~/.bash_profile;
}
if [ "$1" == "--force" -o "$1" == "-f" ]; then
doIt;
else
read -p "This may overwrite existing files in your home directory. Are you sure? (y/n) " -n 1;
echo "";
if [[ $REPLY =~ ^[Yy]$ ]]; then
doIt;
fi;
fi;
unset doIt;
It seems your shell has a problem with the == in the [ command.
The thing is, this operator is undocumented for this old command, so it's possible that some shells won't like it.
The command should work in a modern Bash, for example if you run the script this way:
bash bootstrap.sh
or this way:
./bootstrap.sh
Ideally, the script should not use obscure syntax, for example use this instead:
if [[ $1 == --force || $1 == -f ]]; then
I am writing a bourne shell script that essentially has the same functionality as ls.
Here is my code.
#!/bin/sh
echo "\n"
if [ "$#" -eq 0 ]
then
SEARCH_DIR=`pwd`
fi
if [ "$#" -gt 0 ]
then
SEARCH_DIR=$1
if [ ! -d "$SEARCH_DIR" ]
then
echo "Directory Does Not Exist - - - Exiting"
echo "\n"
exit
fi
fi
DIR_CONTENT=`ls $SEARCH_DIR`
for file in $DIR_CONTENT
do
if [ -f "$file" ]
then
echo "f\c"
fi
if [ -d "$file" ]
then
echo "d\c"
fi
if [ ! -f "$file" ] && [ ! -d "$file" ]
then
echo "-\c"
fi
if [ -r "$file" ]
then
echo "r\c"
else
echo "-\c"
fi
if [ -w "$file" ]
then
echo "w\c"
else
echo "-\c"
fi
if [ -x "$file" ]
then
echo "x\c"
else
echo "-\c"
fi
echo ' \c'
echo "$file"
done
echo "\n"
When I execute the script, I get the desired output for that specific directory:
For example:
$ ./dirinfo
dirinfo version 0.1
drwx Desktop
frwx dirinfo
frw- #dirinfo#
frwx dirinfo~
frwx dirinfo2~
But if I try to pass an argument for a different directory the script doesn't seem to acknowledge my if statements.
For example:
$ ./dirinfo /bin
dirinfo version 0.1
---- bash
---- bunzip2
---- busybox
---- bzcat
---- bzcmp
But if I execute the script from the /bin directory I get the desired effect:
$ cd /bin
$ ~/dirinfo
dirinfo version 0.1
fr-x bash
fr-x bunzip2
fr-x busybox
fr-x bzcat
fr-x bzcmp
Could someone please attempt to point me in the right direction? Thanks!
Dont have a bash to test right now, but maybe $file doesnt have the full path so, evaluating -r or -w would not work. When you cd to destination directory, files are on ./.
Yes, as user430051 mentioned you are running it from a directory and listing files from another which will not work.
solution is prepend you search dir before filename like,
for file in $DIR_CONTENT
do
file="$SEARCH_DIR/$file"
if [ -f "$file" ]
then
echo "f\c"
fi
and it should work.
There can be many ways to solve your problem but the simplest solution would be to add one line which is missing (i.e cd "$SEARCH_DIR"), just add it in your script after DIR_CONTENT=ls $SEARCH_DIR and your script is good to go as per your expectation.
The main difference in this solution and above given by Nachiket is that in my solution file names in output will not have absolute path which I guess is your expectation.
DIR_CONTENT=`ls $SEARCH_DIR`
cd "$SEARCH_DIR"
for file in $DIR_CONTENT
do
if [ -f "$file" ]
I just think that it is convenient for me to "cd" to the directory where I store some file, ie.
[admin#local /]$ cd /usr/bin/somefile.pl
which as far as I know that the official "cd" command will not work.
so I wrote something like this:
main () {
if [[ "${1}" =~ "(.+/)*(.*){1}" ]] && [ -f "${1}" ] ; then
`\cd ${1%/*}`
elif [ -f "${1}" ] ; then
exit 0
else ; `\cd ${1}`
fi
}
main ${1}
and I alias this cd.sh to the "cd" command:
alias cd='source /somepath/cd.sh'
and this doesn't work.
I've tried to use eval "\cd xxx" instead of just \cd xxx;
How can I fix my script?
It feels like a bad idea to override cd, so I'll suggest a slightly different command, fcd:
fcd() { cd -- "$(dirname -- "$1")"; }
$ fcd /usr/bin/somefile.pl
$ pwd
/usr/bin
Or using parameter expansion to save a call to dirname:
fcd { cd -- "${1%/*}"; }
cd() {
DN="$(dirname "$1")"
if [[ -d "$1" ]]; then
builtin cd "$1"
elif [[ -d "$DN" ]]; then
builtin cd "$DN"
else
echo "$* or $DN: No such directories"
return 1
fi
return 0
}
When I get an error like:
$ ls /var/django-projects/daks/public/media/uploads/bandsaws/sneaks.jpg
ls: /var/django-projects/daks/public/media/uploads/bandsaws/sneaks.jpg: No such file or directory
I'd like to be able to ask what-is-the-deepest-path-that-does-exists and get back, say:
/var/django-projects/daks/public/media/
I think it could be done with a loop that added ../ on each iteration and quitted when a path that exists was found.
You may well find dirname useful. Something such as:
f=/var/django-projects/daks/public/media/uploads/bandsaws/sneaks.jpg
until [ -e "$f" ]; do f=$(dirname "$f"); done
echo $f
should give you /var/django-projects/daks/public/media/
Try:
FILE="/var/django-projects/daks/public/media/uploads/bandsaws/sneaks.jpg"
while true; do [ -e "$FILE" ] && break || FILE=$(dirname "$FILE"); done; echo $FILE
#!/bin/bash
function firstValidParent () {
d="$1"
[ -e "${d}" ] && echo $d || firstValidParent "${d%/*}"
}