open file if it exists, otherwise, create a new one and open it - bash

I currently have the following alias set up:
alias emacs='open -a Emacs'
But obviously, if the file doesn't exist already, it gives me an error. Is there a way to change this alias to basically say "if the file exists open it in Emacs, otherwise, create the file and then open it in Emacs?"
Thanks!

Use a function instead of an alias. Functions can do everything aliases can do and much more.
emacs() {
if [ ! -f "$1" ]; then
touch "$1"
fi
open -a Emacs "$1"
}
That should work for one file. For multiple files you can use this.
emacs() {
for file; do
if [ ! -f "$file" ]; then
touch "$file"
fi
done
open -a Emacs "$#"
}

You can consider an alias with --args option for open command like this:
alias emacs='open -a Emacs --args= '
Then call it as:
emacs $PWD/file.txt
As per man open:
--args
All remaining arguments are passed to the opened application in the argv parameter
to main(). These arguments are not opened or interpreted by the open tool.

Or create a small script called omacs with this:
#!/bin/bash
for f in $*; do if [ -f $f ]; then open -a Emacs $f; else touch $f; open -a Emacs $f; fi; done
And create this alias
alias emacs='omacs'
Works with multiple files.

Related

Bash Scripting: How to open Kate with directory and filename?

I am completely new to Bash scripting so bare with me while I try to describe this.
What I want to do be able to insert two arguments.
Arg1=Sub Folder in Documents
Arg2=Title of the text file
Using Ubuntu 20.10. (If that matters)
In short: kate ~/Documents/$Arg1/$Arg2 would be the equivalent to the command I would enter into my terminal.
The extra catch is having a keyword shortcut for the Sub Folder. For example say the Sub folder was name SUPERLONGNAME_EXTRALONG but I want a shortcut such as SHORTNAME=SUPERLONGNAME_EXTRALONG
#!/bin/sh
newfile()
dirname=$1
filename=$2
if $dirname==dir1
dirname=newdirname
fi
kate ~/Documents/$dirname/$filename
This is basically what I have now. Although as you would guess this doesn't work. (Provided for aid in seeing what I am trying to do). I can open kate within the home directory with the file name of my choice. My real issue seems to be the creating a shortcut keyword. As well as having the file save to the Document/Arg1 directory. Please help.
I run the command through terminal using
sh newfile arg1 arg2
Use an associate array shortnames to optionally translate from long to short names. Verify that file and directory exist:
#!/bin/bash
declare -A shortnames=(
[long]="short"
)
d=$1
f=$2
if [ -z "$d" ]
then
echo directory required
exit 1
fi
if [ -n "${shortnames[$d]}" ]
then
d=${shortnames[$d]}
fi
d=~/Documents/$d
if [ ! -d "$d" ]
then
echo "directory $d does not exist"
exit 1
fi
if [ -z "$f" ]
then
echo file required
exit 1
fi
kate "$d/$f"

Read response and if then else loop

I have this piece of rudementary code in my .bash_profile that loads on login, but I can't get it working. Probably some easy fix, but I', staring my self blind on it right now.
The code:
# Simple backup when editing files with nano
function bu() {
read -p "Backup >>"`basename $1`"<< b4 edit [Y/n]?" response
echo $response
response=$response${response,,} # tolower
if [[ $response =~ ^(yes|y| ) ]]; then
mkdir -p ~/.backup
#cp -v "$1" ~/.backup/`basename $1`-`date +%Y%m%d%H%M`.backup
cp "$1" ~/.backup/`basename $1`-`date +%Y%m%d%H%M`.backup
echo ~/.backup/`basename $1`-`date +%Y%m%d%H%M`.backup >> ~/.backup/bu_log.txt
nano "$1"
else
nano "$1"
fi
}
And it has an alias nano="bu"
so, when i write nano, it should ask me if i want to backup the file first (on yes) or just open it in nano straight away.
The only thing that happens now is that it keeps asking the question and looping, newer goes to nano.
CentOS is the linux
Since nano is an alias to bu, typing nano runs your function, which calls nano, which is an alias to bu, which calls nano, ...
In order to run the actual nano editor, you need to disable alias expansion for that call. Use the command built-in:
command nano "$1"
You are calling nano recursivly, since you aliased nano=bu
so try to change the line in the script
nano "$1"
to the full path of
/usr/bin/nano "$1"
(or where nano is installed on your system)
I think you want:
response=${response,,}
You have
response=$response${response,,}
which gets you response=Yy. That won't match your regular expression.
You could also just do shopt -s nocasematch.
Aliases are usually trouble. The rule is "if in doubt, use a function."
nano() {
bu "$#"
}
bu() ( # Use a subshell to avoid having to reset shell options
shopt -s nocasematch
local base=${1##*/}
read -p "Backup >>${base}<< b4 edit [Y/n]?" response
case $response in
y*)
mkdir -p ~/.backup
local backup=~/.backup/"${base}-$(date +%Y%m%d%H%M`).backup"
cp "$1" "$backup"
echo "$backup" >> ~/.backup/bu_log.txt
;;
esac
command nano "$#" # Use "$#" to allow you to pass more than one argument to nano
)

bash save last user input value permanently in the script itself

Is it possible to save last entered value of a variable by the user in the bash script itself so that I reuse value the next time while executing again?.
Eg:
#!/bin/bash
if [ -d "/opt/test" ]; then
echo "Enter path:"
read path
p=$path
else
.....
........
fi
The above script is just a sample example I wanted to give(which may be wrong), is it possible if I want to save the value of p permanently in the script itself to so that I use it somewhere later in the script even when the script is re-executed?.
EDIT:
I am already using sed to overwrite the lines in the script while executing, this method works but this is not at all good practice as said. Replacing the lines in the same file as said in the below answer is much better than what I am using like the one below:
...
....
PATH=""; #This is line no 7
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )";
name="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")";
...
if [ condition ]
fi
path=$path
sed -i '7s|.*|PATH='$path';|' $DIR/$name;
Someting like this should do the asked stuff :
#!/bin/bash
ENTERED_PATH=""
if [ "$ENTERED_PATH" = "" ]; then
echo "Enter path"
read path
ENTERED_PATH=$path
sed -i 's/ENTERED_PATH=""/ENTERED_PATH='$path'/g' $0
fi
This script will ask user a path only if not previously ENTERED_PATH were defined, and store it directly into the current file with the sed line.
Maybe a safer way to do this, would be to write a config file somewhere with the data you want to save and source it . data.saved at the begining of your script.
In the script itself? Yes with sed but it's not advisable.
#!/bin/bash
test='0'
echo "test currently is: $test";
test=`expr $test + 1`
echo "changing test to: $test"
sed -i "s/test='[0-9]*'/test='$test'/" $0
Preferable method:
Try saving the value in a seperate file you can easily do a
myvar=`cat varfile.txt`
And whatever was in the file is not in your variable.
I would suggest using the /tmp/ dir to store the file in.
Another option would be to save the value as an extended attribute attached to the script file. This has many of the same problems as editing the script's contents (permissions issues, weird for multiple users, etc) plus a few of its own (not supported on all filesystems...), but IMHO it's not quite as ugly as rewriting the script itself (a config file really is a better option).
I don't use Linux, but I think the relevant commands would be something like this:
path="$(getfattr --only-values -n "user.saved_path" "${BASH_SOURCE[0]}")"
if [[ -z "$path" ]]; then
read -p "Enter path:" path
setfattr -n "user.saved_path" -v "$path" "${BASH_SOURCE[0]}"
fi

How to change current working directory inside command_not_found_handle

I'm trying to write a not found handle in Bash that does the following:
If $1 exists and it's a directory, cd into it.
If $1 exists inside a user defined directory $DEV_DIR, `cd into it.
If the previous conditions don't apply, fail.
Right now I have something like this:
export DEV_DIR=/Users/federico/programacion/
function command_not_found_handle () {
if [ -d $1 ]; then # the dir exists in '.'
cd $1
else
to=$DEV_DIR$1
if [ -d $to ]; then
cd $to
echo `pwd`
else
echo "${1}: command not found"
fi
fi
}
And although it seems to be working (the echo pwd command prints the expected dir), the directory in the actual shell does not change.
I was under the impression that since this is a function inside my .bashrc the shell wouldn't fork and I could do the cd but apparently that's not working. Any tips on how to solve this would be appreciated.
I think what's going on is that the shell fork()s after setting up any redirections but before looking for commands, so command_not_found_handle can't affect the interactive shell process.
What you seem to want to do may partly possible using the autocd feature:
shopt -s autocd
From man bash:
autocd - If set, a command name that is the name of a directory
is executed as if it were the argument to the cd com‐
mand. This option is only used by interactive shells.
Otherwise, just create a function that you invoke by name that performs the actions you are trying to use command_not_found_handle for.
It won't change directies if you run this program as a script in your main shell because it creates a sub-shell when it executes. If you source the script in your current shell then it will have the desired effect.
~/wbailey> source command_not_found.sh
That said, I think the following would achieve the same result:
wesbailey#feynman:~/code_katas> cd xxx 2> /dev/null || cd ..; pwd
/Users/wesbailey
just replace the ".." with your env var defined directory and create an alias in your .bashrc file.
I've had the very same wish and the solution that I've been using for a while was opening a new tab in gnome terminal by issuing the command gnome-terminal --tab --working-directory="$FOLDER" from inside the command_not_found handle.
But today I've come up with a solution which is not tied to a specific terminal application, but has exactly the intended behaviour.
The solution uses the PROMPT_COMMAND, which is run before each prompt. The PROMPT_COMMAND is bound to a function responsible for checking for a file related to current shell, and cd'ing into the directory specified in that file.
Then, the command_not_found_handle fills in the file when a change in directory is desired. My original command_not_found_handle also checkout a git branch if the current directory is a git repository and the name matches an existing branch. But to keep focus on answering the current question, I've stripped that part of code.
The command_not_found_handle uses find for searching for the directory matching the given name and goes only 2 levels deep in the directory tree, starting from a configured list.
The code to be added to bash_rc follows:
PROMPT_COMMAND=current_shell_cd
CD_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/bash-cd/$$.cd"
current_shell_cd() {
if [ -r "$CD_FILE" ]; then
local CD_TARGET="$( cat "$CD_FILE" )"
[ ! -z "$CD_TARGET" ] && cd "$CD_TARGET" 2>/dev/null
rm "$CD_FILE"
fi
}
command_not_found_handle () {
local COMMAND="$1";
# List folders which are going to be checked
local BASE_FOLDER_LIST=(
"$HOME/Desenvolvimento"
"/var/www/html"
"$HOME/.local/opt/"
)
local FOLDER=$(
find "${BASE_FOLDER_LIST[#]}" \
-maxdepth 2 -type d \
-iname "$COMMAND" -print -quit )
if [ ! -z "$FOLDER" -a -d "$FOLDER" ]
then
mkdir -p "$( dirname "$CD_FILE" )"
echo "$FOLDER" > "$CD_FILE"
else
printf "%s: command not found\n" "$1" 1>&2
return 127
fi
}

Can I specify redirects and pipes in variables?

I have a bash script that creates a Subversion patch file for the current directory. I want to modify it to zip the produced file, if -z is given as an argument to the script.
Here's the relevant part:
zipped=''
zipcommand='>'
if [ "$1" = "-z" ]
then
zipped='zipped '
filename="${filename}.zip"
zipcommand='| zip >'
fi
echo "Creating ${zipped}patch file $filename..."
svn diff $zipcommand $filename
This doesn't work because it passes the | or > contained in $zipcommand as an argument to svn.
I can easily work around this, but the question is whether it's ever possible to use these kinds of operators when they're contained in variables.
Thanks!
I would do something like this (use bash -c or eval):
zipped=''
zipcommand='>'
if [ "$1" = "-z" ]
then
zipped='zipped '
filename="${filename}.zip"
zipcommand='| zip -#'
fi
echo "Creating ${zipped}patch file $filename..."
eval "svn diff $zipcommand $filename"
# this also works:
# bash -c "svn diff $zipcommand $filename"
This appears to work, but my version of zip (Mac OS X) required that i change the line:
zipcommand='| zip -#'
to
zipcommand='| zip - - >'
Edit: incorporated #DanielBungert's suggestion to use eval
eval is what you are looking for.
# eval 'printf "foo\nbar" | grep bar'
bar
Be careful with quote characters on that.
Or you should try zsh shell whic allows to define global aliases, e.g.:
alias -g L='| less'
alias -g S='| sort'
alias -g U='| uniq -c'
Then use this command (which is somewhat cryptic for the ones who took a look from behind ;-) )
./somecommand.sh S U L
HTH
Open a new file handle on either a process substitution to handle the compression or on the named file. Then redirect the output of svn diff to that file handle.
if [ "$1" = "-z" ]; then
zipped='zipped '
filename=$filename.zip
exec 3> >(zip > "$filename")
else
exec 3> "$filename"
fi
echo "Creating ${zipped}patch file $filename"
svn diff >&3

Resources