Override mkdir in bash / shell - bash

for more comfort I like to override mkdir like this:
mkdir() {
if [[ "$#" == *--parents* ]]; then
builtin mkdir "$#"
else
builtin mkdir "$#" --parents
fi
}
Unfortunately there is no builtin of mkdir. How can I do a workaround that does the job?

You can use the command built-in instead:
mkdir() {
if [[ "$#" == *--parents* ]]; then
command mkdir "$#"
else
command mkdir "$#" --parents
fi
}

mkdir_p=`which mkdir`
after that you can call it with:
$mkdir_p args...
or
alias mkdir=¨mkdir -p¨

Related

How can I create a shell alias for creating a new script and opening it in the editor?

Shell script shortcuts or aliases are very useful for automating repetitive tasks in very few key strokes.
One example of example of "shortcut" command is take from zsh which does mkdir $dir && cd $dir.
I want to make something similar (medit) that does this: when called medit script.sh:
creates script
makes it executable (chmod +x)
populates it with shebang line
opens the file in the default editor
Can I do this using an alias so I would avoid writing a bash script that does that?
You better write a function like:
function medit(){
echo "#!/bin/bash" > "$1"
chmod +x "$1"
if [ -n "$VISUAL" ]; then
"$VISUAL" "$1"
elif [ -n "$EDITOR" ]; then
"$EDITOR" "$1"
else
vi "$1"
fi
}
Put it to .bashrc and call it with medit script.sh
It will first try to run the editor specified in $VISUAL and $EDITOR and falls back to vi if there is no standard editor specified.
I would write something like this:
# Usage: just like 'builtin printf'
fatal_error() {
builtin printf "$#" >&2
exit 1
}
# Usage: medit [file] [more arguments]
medit() {
# If we have arguments
if [ $# -gt 0 ]; then
# If $1 file does not exist, or is empty,
# put a shebang line into it. See `help test`.
if [ ! -s "$1" ]; then
printf "%s\n" "#!/bin/bash -" > "$1" || \
fatal_error "Failed to write to %s\n" "$1"
fi
# Make $1 executable
chmod +x "$1" || fatal_error "failed to chmod %s\n" "$1"
fi
# Run the default editor with the arguments passed to this function
"${EDITOR:-${VISUAL:-vi}}" "$#"
}
The medit command can be invoked just like the default editor, even without arguments.
On the off-chance that you don't know how to re-use the script above, put the code into some ~/scripts/medit, and source it from the appropriate initialization script such as ~/.bashrc: source ~/scripts/medit.
What you are asking for is an alias.
There is no way to do all you ask without using one or several functions.
But there is a way to make an alias define some functions and also call them:
alias medit='
SayError(){ local a=$1; shift; printf "%s\n" "$0: $#" >&2; exit "$a"; }
medit(){
[[ $# -lt 1 ]] &&
SayError 1 "We need at least the name of the file as an argument"
[[ ! -s $1 ]] && echo "#!/bin/bash" > "$1" ||
SayError 2 "File $1 already exists"
chmod u+x "$1" ||
SayError 3 "File $1 could not be made executable"
${VISUAL:-${EDITOR:-emacs}} "$1" ||
SayError 4 "File $1 could not be open in the editor"
}
\medit'
You need to execute the above definition of the alias medit or place it in ~/.bashrc, or simply source it in the running shell to make it exist.
Then, when the alias is called, it defines two functions: SayError and medit.
Yes, a function with the same name as the alias: medit.
After the function definition, the alias will call the function by using a trick:
\medit
As (strictly speaking) a \medit is not exactly the alias medit, bash keeps searching and finds the function medit, which by then has been defined and is executed.
Of course, you can just define the functions and use them without resorting to an alias to make the definition of the functions, that's your choice.
What is nice is to have the choice. :)
This is how you could define all in a sourced file:
alias medit='\medit'
SayError(){ local a=$1; shift; printf "%s\n" "$0: $#" >&2; exit "$a"; }
medit(){
[[ $# -lt 1 ]] &&
SayError 1 "We need at least the name of the file as an argument"
[[ ! -s $1 ]] && echo "#!/bin/bash" > "$1" ||
SayError 2 "File $1 already exists"
chmod u+x "$1" ||
SayError 3 "File $1 could not be made executable"
${VISUAL:-${EDITOR:-emacs}} "$1" ||
SayError 4 "File $1 could not be open in the editor"
}

What's wrong with this bash negation of a logical expression?

This bash doesn't parse
if ! [[ mkdir -p "$available"
&& mkdir -p "$enabled"
&& mkdir -p "$logroot"
&& chmod 755 "$available" "$enabled" "$logroot" ]]
then
echo "Could not make $available, $enabled, and $logroot."
exit 1;
fi
What's the correct form?
If you want to execute multiple commands chained with &&, then [[ ... ]] is not appropriate.
It seem this is what you intended:
if ! { mkdir -p "$available" &&
mkdir -p "$enabled" &&
mkdir -p "$logroot" &&
chmod 755 "$available" "$enabled" "$logroot"; }
then
echo "Could not make $available, $enabled, and $logroot."
exit 1
fi
To answer your follow-up question, the equivalent of if(!(a && !b && c)) would be:
if ! (a && ! b && c); then
# ...
fi
That is, ! binds only to the term next to it,
and you need to put a space around !. (Thanks #that-other-guy)
Or even better, you can avoid a (...) subshell by using grouping within { ...; } instead (thanks #charles-duffy):
if ! { a && ! b && c; }; then
# ...
fi
You can use it like this in BASH:
if ! { mkdir -p "$available" &&
mkdir -p "$enabled" &&
mkdir -p "$logroot" &&
chmod 755 "$available" "$enabled" "$logroot"; }
then
echo "Could not make $available, $enabled, and $logroot."
fi
&& allows you to enter next command in newline
Instead of ! [[ ... ]] you should use ! { ... } as [[ ... ]] is used for evaluating conditional expressions.

shell script error: bootstrap.sh:13: = not found

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

Customize "cd" in bash

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
}

Bash: passing around compound commands

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"

Resources