How can I execute the specific line I just printed in zsh? - macos

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")
}

Related

Make fzf `**<TAB>` completion only return files for specific command (caveat inside!)

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"
}

How to split the file path to extract the various subfolders into variables? (Ubuntu Bash)

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

Check if directory is /Library or /Public

I am trying to write a bash script which takes a users home directory and cycles through the first level of subdirectories and performs some maintenance on those directories only if it is not the /Library or /Public folder. The code I have so far does not work as I get an error message saying that the directory name returned by $dir is a directory. Here is the code:
#!/bin/bash
user="short name"
source_root="/Users/"
source_use="$source_root$user"
cd "$source_use"
dirarr=( */ )
echo ${dirarr[#]}
for dir in "${dirarr[#]}"
do
if ( "$dir" -ne "/Library" -o "$dir" -ne "/Public")
then echo $dir.
# do something
fi
done
Can anyone help me get this working.
Many thanks
Your script has several problems:
You need to use [ ] or [[ ]] in your if statement, not ( ). In your example ( ) creates a subshell and tries to run a command "$dir", which is the reason you're getting the error message you see.
You're comparing against strings that you won't find - try "Library/" and "Public/" instead.
You probably want -a instead of -o.
-ne is used to compare numbers. You want !=.
Here's a corrected version of your script:
#!/bin/bash
user="short name"
source_root="/Users/"
source_use="$source_root$user"
cd "$source_use"
dirarr=( */ )
echo ${dirarr[#]}
for dir in "${dirarr[#]}"
do
if [ "$dir" != "Library/" -a "$dir" != "Public/" ]
then
echo $dir.
# do something
fi
done
Try this:
cd $source_root$user
for dir in `find . -maxdepth 1 -type d`
do
if [ $dir = ./Library ] || [ $dir = ./Public ]
then
continue
fi
(Perform actions)
done
Also, bash is backwards. != is string non-equality, -ne is integer non-equality. So, change to equals signs, too.
Good luck!

Two bash aliases, same function

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

Find file by name up the directory tree, using bash

Using bash, how can I find a file with a specific name somewhere up the directory tree from the pwd?
To be more clear. I want to find a file that sits in my working directory's root, but I don't know where the root is, and my pwd might be anywhere below the root.
Find file.txt up to root
x=`pwd`
while [ "$x" != "/" ] ; do
x=`dirname "$x"`
find "$x" -maxdepth 1 -name file.txt
done
local DIR=$(pwd)
while [ ! -z "$DIR" ] && [ ! -f "$DIR/myFile.txt" ]; do
DIR="${DIR%\/*}"
done
echo $DIR/myFile.txt
I have the following function defined in my ~/.bashrc:
dnif () {
# Recursively list a file from PWD up the directory tree to root
[[ -n $1 ]] || { echo "dnif [ls-opts] name"; return 1; }
local THERE=$PWD RC=2
while [[ $THERE != / ]]
do [[ -e $THERE/${2:-$1} ]] && { ls ${2:+$1} $THERE/${2:-$1}; RC=0; }
THERE=$(dirname $THERE)
done
[[ -e $THERE/${2:-$1} ]] && { ls ${2:+$1} /${2:-$1}; RC=0; }
return $RC
}
which will search for a name you provide as a parameter in each directory upwards from the current to the root, and if found, list it with 'ls' and the optional ls -options that you provide. Example output:
me#host:~/dev/example
$ dnif; echo $?
dnif [ls-opts] name
1
me#host:~/dev/example
$ dnif -alp nonesuch; echo $?
2
me#host:~/dev/example
$ dnif -alp .bashrc; echo $?
-rw-r--r-- 1 me mine 3486 Apr 3 2012 /home/me/.bashrc
0
me#host:~/dev/example
$ dnif -d .
/home/me/dev/example/.
/home/me/dev/.
/home/me/.
/home/.
/.
Please note:
"dnif" is "find" backwards.
The function is a finite loop (not recursive), creates no subshells, and uses Bash built-ins as much as possible for speed.
All hits at each ascending directory level are listed.
The ls -opts are optional, but must precede the required search argument.
The search argument may be a file or directory.
If the search argument is a directory, include the ls -opt '-d' to restrict the results to directory names rather than contents.
The function returns exit code
0 if there is at least one hit,
1 if no parameters are provided for help, and
2 if nothing is found.

Resources