I have a very simple function
function filefinder
{
FILE=$1; test -f $FILE || FILE=`find . -name $FILE`; /usr/bin/geany $FILE
}
alias geany=filefinder
But I would like other editors to use the same function. How can I do that? Say, I would like to add alias nano=filefinder. Obviously I wouldn't like to hardwire either geany or nano.
How about:
function filefinder
{
FILE=$2; ED=$1; test -f $FILE || FILE=`find . -name $FILE`; /usr/bin/$ED $FILE;
}
alias geany="filefinder geany"
alias gedit="filefinder gedit"
alias nano="filefinder nano"
I assumed your distro keeps all the execs in /usr/bin
The usual way to do this sort of thing is with the variables that are commonly used to hold the name of the user's preferred editor.
function filefinder {
editor=${VISUAL:-$EDITOR}; editor=${editor:-emacs}; FILE=$1; test -f "$FILE" || FILE=$(find . -name "$FILE"); "$editor" "$FILE"
}
This follows what Bash does when you use the readline function edit-and-execute-command.
If $VISUAL is set use it, if not use $EDITOR. It that's not set, use emacs as the fallback (you could use something else if you want, of course).
How about an environment variable?:
function filefinder
{
if [ -z "$MY_EDITOR" ];then
echo "MY_EDITOR is not set";
else
FILE=$1; test -f $FILE || FILE=`find . -name $FILE`; $MY_EDITOR $FILE
fi
}
alias myedit=filefinder
Related
I'm trying to make fzfs autocompletion invoked on cat **<TAB> return only files (directories does not make sense for cat). According to docs it's the matter of defining _fzf_complete_cat(), so my first try was
_fzf_complete_cat() {
_fzf_complete -- "$#" < <(
fd --type f --hidden --follow --no-ignore --exclude .git
)
}
however, there's a major flaw: this completion does not take into account the path's part before **, e.g. if I am in ~/subdir and type cat ~/another/**<TAB> it will not recurse from there, but rather from ~/subdir.
I understand the trick is around these lines from fzf/shell/completion.zsh, namely, the way _fzf_[path|dir]_completion() handles it. So I tried smth like that (essentially, repeating the logic of the completion.zsh):
_custom_fzf_compgen_path() {
fd --hidden --follow --no-ignore --exclude ".git" . "$1"
}
_custom_fzf_path_completion() {
__fzf_generic_path_completion "$1" "$2" _fzf_compgen_path \
"-m" "" " "
}
_fzf_complete_cat() {
_custom_fzf_path_completion "$prefix" "$1"
}
and it almost works, messing up in the very end with zle reset-prompt from here.
TL;DR: I need **<TAB> for cat command work similarly to _fzf_[path|dir]_completion() but return only files.
Appreciate any ideas! Thanks!
I ended up going down the initial path of tweaking _fzf_complete_cat(). It uses a wonderful function __fzf_generic_path_completion() which takes care of splitting the path yped before **<TAB> (e.g. cat ~/some_other_dir/**<TAB>) and finding the base of it. Problem lied around extra zle reset-prompt in here, so I had to create my own __fzf_generic_path_completion() without it.
Full working solution
__custom_fzf_generic_path_completion() {
local base lbuf cmd compgen fzf_opts suffix tail dir leftover matches
base=$1
lbuf=$2
cmd=$(__fzf_extract_command "$lbuf")
compgen=$3
fzf_opts=$4
suffix=$5
tail=$6
setopt localoptions nonomatch
eval "base=$base"
[[ $base = *"/"* ]] && dir="$base"
while [ 1 ]; do
if [[ -z "$dir" || -d ${dir} ]]; then
leftover=${base/#"$dir"}
leftover=${leftover/#\/}
[ -z "$dir" ] && dir='.'
[ "$dir" != "/" ] && dir="${dir/%\//}"
matches=$(eval "$compgen $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" | while read item; do
echo -n "${(q)item}$suffix "
done)
matches=${matches% }
if [ -n "$matches" ]; then
LBUFFER="$lbuf$matches$tail"
fi
# REMOVED HERE zle reset-prompt
break
fi
dir=$(dirname "$dir")
dir=${dir%/}/
done
}
_custom_fzf_compgen_path() {
# Add here your favorite command to search for files. $1 is the starting
# point.
fd --type f --hidden --follow --no-ignore --exclude ".git" . "$1"
}
_custom_fzf_path_completion() {
__custom_fzf_generic_path_completion "$1" "$2" _custom_fzf_compgen_path \
"-m" "" " "
}
_fzf_complete_cat() {
# $prefix is the variable set by fzf here https://github.com/junegunn/fzf/blob/master/shell/completion.zsh#L302
_custom_fzf_path_completion "$prefix" "$1"
}
This is (or was, at least) a common pattern in Ruby, but I can't figure out how to do it in Zsh or Bash.
Let's suppose I have a shell function called "whoosiwhatsit", and I want to override it in a specific project, while still keeping the original available under a different name.
If I didn't know better, I might try creating an alias to point to whoosiwhatsit, and then create a new "whoosiwhatsit" function that uses the alias. Of course that work, since the alias will refer to the new function instead.
Is there any way to accomplish what I'm talking about?
Aliases are pretty weak. You can do this with functions though. Consider the following tools:
#!/usr/bin/env bash
PS4=':${#FUNCNAME[#]}:${BASH_SOURCE}:$LINENO+'
rename_function() {
local orig_definition new_definition new_name retval
retval=$1; shift
orig_definition=$(declare -f "$1") || return 1
new_name="${1}_"
while declare -f "$new_name" >/dev/null 2>&1; do
new_name+="_"
done
new_definition=${orig_definition/"$1"/"$new_name"}
eval "$new_definition" || return
unset -f "$orig_definition"
printf -v "$retval" %s "$new_name"
}
# usage: shadow_function target_name shadowing_func [...]
# ...replaces target_name with a function which will call:
# shadowing_func target_renamed_to_this number_of_args_in_[...] [...] "$#"
shadow_function() {
local shadowed_func eval_code shadowed_name shadowing_func shadowed_func_renamed
shadowed_name=$1; shift
shadowing_func=$1; shift
rename_function shadowed_func_renamed "$shadowed_name" || return
if (( $# )); then printf -v const_args '%q ' "$#"; else const_args=''; fi
printf -v eval_code '%q() { %q %q %s "$#"; }' \
"$shadowed_name" "$shadowing_func" "$shadowed_func_renamed" "$# $const_args"
eval "$eval_code"
}
...and the following example application of those tools:
whoosiwhatsit() { echo "This is the original implementation"; }
override_in_directory() {
local shadowed_func=$1; shift
local override_cmd_len=$1; shift
local override_dir=$1; shift
local -a override_cmd=( )
local i
for (( i=1; i<override_cmd_len; i++)); do : "$1"
override_cmd+=( "$1" ); shift
done
: PWD="$PWD" override_dir="$override_dir" shadowed_func="$shadowed_func"
: override_args "${override_args[#]}"
if [[ $PWD = $override_dir || $PWD = $override_dir/* ]]; then
[[ $- = *x* ]] && declare -f shadowed_func >&2 # if in debugging mode
"${override_cmd[#]}"
else
"$shadowed_func" "$#"
fi
}
ask_the_user_first() {
local shadowed_func=$1; shift;
shift # ignore static-argument-count parameter
if [[ -t 0 ]]; then
read -r -p "Press ctrl+c if you are unsure, or enter if you are"
fi
"$shadowed_func" "$#"
}
shadow_function whoosiwhatsit ask_the_user_first
shadow_function whoosiwhatsit \
override_in_directory /tmp echo "Not in the /tmp!!!"
shadow_function whoosiwhatsit \
override_in_directory /home echo "Don't try this at home"
The end result is a whoosiwhatsit function that asks the user before it does anything when its stdin is a TTY, and aborts (with different messages) when run under either /tmp or /home.
That said, I don't condone this practice. Consider the above provided as an intellectual exercise. :)
In bash, there is a built-in variable called BASH_ALIASES that is an associative array containing the current aliases. The semantics are a bit inconsistent when you update it (RTM!) but if you restrict yourself to reading BASH_ALIASES, you should be able to write yourself a shell function that implements alias chaining.
It's common and well supported to create a single level of overrides through functions that optionally invoke their overridden builtin or command:
# Make all cd commands auto-exit on failure
cd() { builtin cd "$#" || exit; }
# Make all ssh commands verbose
ssh() { command ssh -vv "$#"; }
It doesn't chain beyond the one link, but it's completely POSIX and often works better in practice than trying to write Ruby in Bash.
Say I have searched the path for some files with mdfind. Now I have several addresses printed and what I want to do is cd into the first address. Is there any way writing a shell script or some thing that could facilitate me getting into the path I want without copy and paste?
xxx#MacBook-Pro ~ mdfind -name "adapters.py"
/usr/local/Cellar/python/2.7.10_2/libexec/pip/build/lib/pip/_vendor/requests/adapters.py
/usr/local/lib/python2.7/site-packages/pip/_vendor/requests/adapters.py
/usr/local/lib/python3.4/site-packages/pip/_vendor/requests/adapters.py
/usr/local/Cellar/python/2.7.10_2/libexec/pip/pip/_vendor/requests/adapters.py
/usr/local/lib/python2.7/site-packages/pip/_vendor/requests/adapters.pyc
A quick way would be nesting command substitutions
cd $(dirname $(mdfind -name "adapters.py" | head -n 1))
A function could also define in your .zshrc
func mdcd() {
if [ -z "$1" ]; then return 64; fi
1=$(mdfind -name "$1" | head -n 1)
if [ -z "$1" ]; then return 66; fi
cd $(dirname "$1")
}
I wanted to create the alias for the a bash script that make use of wild card as an argument. When I am trying to make use of the alias cmd its not giving the required output.
The usage of the alias cmd will be log_list /tmp/abc*
#usage log_list /tmp/abc*
alias log_list=`sh scriptname $1`
#here is the script code
for file_name in $* ; do
if [ ! -d $file_name ] && [ -f $file_name ] ; then
#do some processing ...
echo $file_name
fi
done
Aliases don't handle arguments via $1 and the like; they just prepend their text directly to the rest of the command line.
Either use:
alias log_list='sh scriptname' # note that these are single-quotes, not backticks
...or a function:
log_list() { sh scriptname "$#"; }
...though if your script is named log_list, marked executable, and located somewhere in the PATH, that alias or function should be completely unnecessary.
Now, that said, your proposed implementation of log_list also has a bunch of bugs. A cleaned-up version might look more like...
#!/bin/sh
for file_name in "$#" ; do
if [ ! -d "$file_name" ] && [ -f "$file_name" ] ; then
#do some processing ...
printf '%s\n' "$file_name"
fi
done
I need help with Ubuntu Precise bash script.
I have several tiff files in various folders
masterFOlder--masterSub1 --masterSub1-1 --file1.tif
|--masterSub1-2 --masterSub1-2-1 --file2.tif
|
|--masterSub2 --masterSub1-2 .....
I need to run an Imagemagick command and save them to new folder "converted" while retaining the sub folder tree i.e. the new tree will be
converted --masterSub1 --masterSub1-1 --file1.png
|--masterSub1-2 --masterSub1-2-1 --file2.png
|
|--masterSub2 --masterSub1-2 .....
How do i split the filepath into folders, replace the first folder (masterFOlder to converted) and recreate a new file path?
Thanks to everyone reading this.
This script should work.
#!/bin/bash
shopt -s extglob && [[ $# -eq 2 && -n $1 && -n $2 ]] || exit
MASTERFOLDER=${1%%+(/)}/
CONVERTFOLDER=$2
OFFSET=${#MASTERFOLDER}
while read -r FILE; do
CPATH=${FILE:OFFSET}
CPATH=${CONVERTFOLDER}/${CPATH%.???}.png
CDIR=${CPATH%/*}
echo "Converting $FILE to $CPATH."
[[ -d $CDIR ]] || mkdir -p "$CDIR" && echo convert "$FILE" "$CPATH" || echo "Conversion failed."
done < <(exec find "${MASTERFOLDER}" -mindepth 1 -type f -iname '*.tif')
Just replace echo convert "$FILE" "$CPATH" with the actual command you use and run bash script.sh masterfolder convertedfolder