How to port `ranger-cd` function to fish shell - fish

I have been trying to port the ranger-cd function for the ranger file manager to the fish shell. As of 2013, ranger’s ranger-cd function looks like this:
function ranger-cd {
tempfile='/tmp/chosendir'
/usr/bin/ranger --choosedir="$tempfile" "${#:-$(pwd)}"
test -f "$tempfile" &&
if [ "$(cat -- "$tempfile")" != "$(echo -n `pwd`)" ]; then
cd -- "$(cat "$tempfile")"
fi
rm -f -- "$tempfile"
}
# This binds Ctrl-O to ranger-cd:
bind '"\C-o":"ranger-cd\C-m"'
(This function gives a temporary file to ranger file manager to store the last accessed directory so that we can change to that directory after ranger quits.)
Here’s what I have done so far to port the function to fish:
function ranger-cd
set tempfile '/tmp/chosendir'
/usr/bin/ranger --choosedir=$tempfile (pwd)
test -f $tempfile and
if cat $tempfile != echo -n (pwd)
cd (cat $tempfile)
end
rm -f $tempfile
end
function fish_user_key_bindings
bind \co ranger-cd
end
When I use this function I get:
test: unexpected argument at index 2: 'and'
1 /home/gokcecat: !=: No such file or directory
cat: echo: No such file or directory
cat: /home/gokce: Is a directory
I’m guessing there are still multiple errors in the above code. Does anyone have a working solution for this?

My answer is based off of gzfrancisco's. However, I fix the "'-a' at index 2" issue, and I also ensure that a new prompt is printed after exiting ranger.
I put the following in ~/.config/fish/config.fish:
function ranger-cd
set tempfile '/tmp/chosendir'
ranger --choosedir=$tempfile (pwd)
if test -f $tempfile
if [ (cat $tempfile) != (pwd) ]
cd (cat $tempfile)
end
end
rm -f $tempfile
end
function fish_user_key_bindings
bind \co 'ranger-cd ; commandline -f repaint'
end

simpler solution, but it does the same. type in shell:
alias ranger 'ranger --choosedir=$HOME/.rangerdir; set RANGERDIR (cat $HOME/.rangerdir); cd $RANGERDIR'
funcsave ranger
alias is a shortcut do define a function. funcsave saves it into ~/.config/fish/functions/ranger.fish

The problem is this line.
test -f $tempfile and
You shuld remove the and because "and" is a conditional execution
http://ridiculousfish.com/shell/user_doc/html/commands.html#and
function ranger-cd
set tempfile '/tmp/chosendir'
/usr/bin/ranger --choosedir=$tempfile (pwd)
if test -f $tempfile and cat $tempfile != echo -n (pwd)
cd (cat $tempfile)
end
rm -f $tempfile
end

EDIT: This does not work for fish unfortunately, but only for bash-compatible shells.
I recently found out that there is another way to accomplish the same end goal, by sourcing the ranger script instead:
source ranger
When ranger is then exited, it will have changed the working directory into the last visited one.

Related

sh Script with copy and removing a part from filename

So i use a script that copy's specific files to specific folders based on there filenames. I would like to extend the script so that after the copy progress a part from the filename is removed. Here is an example The filename looks likes this Image_000058_19_12_2019_1920x1080.jpg and i like to remove the resolution (_1920x1080) part from it. Is there a way to add it to my Script (see below) Thanks to #fedxc for the script.
cd "$HOME/Downloads"
# for filename in *; do
find . -type f | while IFS= read filename; do # Look for files in all ~/Download sub-dirs
case "${filename,,*}" in # this syntax emits the value in lowercase: ${var,,*} (bash version 4)
*.part) : ;; # Excludes *.part files from being moved
move.sh) : ;;
# *test*) mv "$filename" "$HOME/TVshows/Glee/" ;; # Using move there is no need to {&& rm "$filename"}
*test*) scp "$filename" "imac#imac.local:/users/imac/Desktop/" && rm "$filename" ;;
*american*dad*) scp "$filename" "imac#imac.local:/users/imac/Movies/Series/American\ Dad/" && rm "$filename" ;;
*) echo "Don't know where to put $filename" ;;
esac
done```
I use variable operation for bash. Example:
export filename='Image_000058_19_12_2019_1920x1080.jpg' <----Setting name of filename
echo ${filename/_1920x1080/} <--Operation with bash variable.
Image_000058_19_12_2019.jpg <--Result of echo
Consult this page for more: Bash Guide

Bourne Shell Script

I'm attempting to write a script in the Bourne shell that will do the following:
Read in a filename
If the file does not exist in the target directory, it will display a message to the user stating such
If the file exists in the target directory, it will be moved to a /trash folder
If the file exists in the target directory, but a file of the same name is in the /trash folder, it will still move the file to the /trash directory, but will attach a _bak extention to the file.
My use of the Bourne shell is minimal, so here's what I have so far. Any pointers or tips would be greatly appreciated, thanks!
#!/bin/sh
#Scriptname: Trash Utility
source_dir=~/p6_tmp
target_dir=~/trash
echo "Please enter the filename you wish to trash:"
read filename
if [ -f $source_dir $filename]
then mv "$filename" "$target_dir"
else
echo "$filename does not exist"
fi
You cannot use ~ to refer to $HOME in a sh script. Switch to $HOME (or change the shebang to a shell which supports this, such as #!/bin/bash).
To refer to a file in a directory, join them with a slash:
if [ -f "$source_dir/$filename" ]
Notice also the required space before the terminating ] token.
To actually move the file you tested for, use the same expression for the source argument to mv:
mv "$source_dir/$filename" "$target_dir"
As a general design, a script which takes a command-line parameter is much easier to integrate into future scripts than one wich does interactive prompting. Most modern shells offer file name completion and history mechanisms, so a noninteractive script also tends to be more usable (you practically never need to transcribe a file name manually).
A Bash Solution:
#!/bin/bash
source_dir="~/p6_tmp"
target_dir="~/trash"
echo "Please enter the filename you wish to trash:"
read filename
if [ -f ${source_dir}/${filename} ]
then
if [ -f ${target_dir}/${filename} ]
then
mv "${source_dir}/${filename}" "${target_dir}/${filename}_bak"
else
mv "${source_dir}/${filename}" "$target_dir"
fi
else
echo "The file ${source_dir}/${filename} does not exist"
fi
Here's the completed script. Thanks again to all who helped!
#!/bin/sh
#Scriptname: Trash Utility
#Description: This script will allow the user to enter a filename they wish to send to the trash folder.
source_dir=~/p6_tmp
target_dir=~/trash
echo "Please enter the file you wish to trash:"
read filename
if [ -f "$source_dir/$filename" ]
then
if [ -f "$target_dir/$filename" ]
then mv "$source_dir/$filename" "$target_dir/$(basename "$filename")_bak"
date "+%Y-%m-%d %T - Trash renamed ~/$(basename "$source_dir")/$filename to ~/$(basename "/$target_dir")/$(basename "$filename")_bak" >> .trashlog
else mv "$source_dir/$filename" "$target_dir"
date "+%Y-%m-%d %T - Trash moved ~/$(basename "/$source_dir")/$filename to ~/$(basename "/$target_dir")/$filename" >> .trashlog
fi
else
date "+%Y-%m-%d %T - Trash of ~/$(basename "/$source_dir")/$filename does not exist" >> .trashlog
fi

Can I split a large HAProxy config file into multiple smaller files?

I'm building an haproxy config file that has multiple front and backends. It's going to be several hundred lines long and I'd rather split it up into separate files for each of the different websites that I want to loadbalance.
Does HAProxy offer the ability to link to partial config files from the main haproxy.cfg file?
Configuration files can't be linked together from a configuration directive.
However HAProxy can load multiple configuration files from its command line, using the -f switch multiple times:
haproxy -f conf/http-defaults -f conf/http-listeners -f conf/tcp-defaults -f conf/tcp-listeners
If you want to be flexible with the amount of config files you can even specify a directory like this: -f /etc/haproxy. The files will then be used in their lexical order, newer files overriding older files.
See the mailing list for an example, if provides links to the documentation. This information can be found in the management guide, not the regular docs.
Stumbled on this answer where the author created scripts to imitate nginx disable enable sites functionality. In the haproxy init.d startup he uses script loop to build the haproxy -f commands concatenation.
/etc/init.d/haproxy:
EXTRAOPTS=`for FILE in \`find /etc/haproxy/sites-enabled -type l | sort
-n\`; do CONFIGS="$CONFIGS -f $FILE"; done; echo $CONFIGS`
haensite script:
#!/bin/bash
if [[ $EUID -ne 0 ]]; then
echo "You must be a root user" 2>&1
exit 1
fi
if [ $# -lt 1 ]; then
echo "Invalid number of arguments"
exit 1
fi
echo "Enabling $1..."
cd /etc/haproxy/sites-enabled
ln -s ../sites-available/$1 ./
echo "To activate the new configuration, you need to run:"
echo " /etc/init.d/haproxy restart"
hadissite script:
#!/bin/bash
if [[ $EUID -ne 0 ]]; then
echo "You must be a root user" 2>&1
exit 1
fi
if [ $# -lt 1 ]; then
echo "Invalid number of arguments"
exit 1
fi
echo "Disabling $1..."
rm -f /etc/haproxy/sites-enabled/$1
echo "To activate the new configuration, you need to run:"
echo " /etc/init.d/haproxy restart"
This was a solution building off of #stephenmurdoch's answer which involved the use of multiple -f <conf file> arguments to the haproxy executable.
Using the stock CentOS 6.x RPM's included /etc/init.d/haproxy script you can amend it like so:
start() {
$exec -c -q -f $cfgfile $OPTIONS
if [ $? -ne 0 ]; then
echo "Errors in configuration file, check with $prog check."
return 1
fi
echo -n $"Starting $prog: "
# start it up here, usually something like "daemon $exec"
#daemon $exec -D -f $cfgfile -f /etc/haproxy/haproxy_ds.cfg -f /etc/haproxy/haproxy_es.cfg -f /etc/haproxy/haproxy_stats.cfg -p $pidfile $OPTIONS
daemon $exec -D -f $cfgfile $(for i in /etc/haproxy/haproxy_*.cfg;do echo -n "-f $i ";done) -p $pidfile $OPTIONS
retval=$?
echo
[ $retval -eq 0 ] && touch $lockfile
return $retval
}
With the above in place you can then create files such as haproxy_<X>.cfg and haproxy_<Y>.cfg using whatever names you want. The above for loop will include these files in an augmented daemon haproxy ... line if these files are present, otherwise the stock haproxy.cfg file will be used solely.
Within the haproxy_<...>.cfg files you need to make sure that your global and defaults are defined in the "toplevel" haproxy.cfg file. The rest of the files simply need to have frontend/backends and nothing more.
You can follow this simple step.
Insert one line script (cat /etc/$BASENAME/conf.d/*.cfg > $CFG) in /etc/init.d/haproxy
Here is position where you must insert line
CFG=/etc/$BASENAME/$BASENAME.cfg
cat /etc/$BASENAME/conf.d/*.cfg > $CFG
[ -f $CFG ] || exit 1
Reload daemon config with systemctl daemon-reload
Make directory mkdir /etc/haproxy/conf.d
Move default haproxy.cfg to conf.d as global.cfg mv /etc/haproxy/haproxy.cfg /etc/haproxy/conf.d/global.cfg
Create your other .cfg file in conf.d directory
Just restart your haproxy service systemctl restart haproxy
NOTE: /etc/haproxy/haproxy.cfg will be automaticly created from all files in conf.d/
answer of #Bapstie memtioned that, a directory can be passed to haproxy as config file, and files inside will be loaded in alphabet order. It's correct.
But problem is, the package haproxy in CentOS 'base/7/x86_64' repository is so old that it does not support that.
So either do you need to write a wrapper to append -f <individual config file>to the command, or you need to install latest version of haproxy:
for package in centos-release-scl-rh rh-haproxy18-haproxy; do
yum install -y $package
done
and create a drop-in config for haproxy service:
[Service]
ExecStart=
ExecStart=/opt/rh/rh-haproxy18/root/sbin/haproxy -f /etc/haproxy-nutstore/ -p /run/haproxy.pid $OPTIONS
If you use Ansible you can do a trick like this:
- name: haproxy configuration
copy:
content: >
{{ lookup('template', haproxy_cfg_src_top) +
lookup('template', haproxy_cfg_src_edge) +
lookup('template', haproxy_cfg_src_bottom) }}
dest: "{{ haproxy_cfg }}"
owner: "{{ docker_user }}"
group: "docker"
mode: 0664
register: haproxy_cfg_change

pipe into conditional on command line

I have a problem i could not figure out if it's even possible. I am parsing a file with filenames in it, and want to check if those filenames represent an existing file within the system.
i figured out a possibility to to check if a file exists:
[ -f FILENAME ] && echo "File exists" || echo "File does not exists"
now my problem is: How can i pipe into to the conditional that it tests for all the filenames?
i was trying like tihs, but it did not work:
cat myfilenames.txt | xargs command from above without FILENAME
does anybody know if it is possible?
thanks, dmeu!
while read file; dp
[ -e "$file" ] && echo "$file exists";
done <filelist.txt
I believe what you want is a for loop. This worked for me in bash (I put it in a shell script, but you could probably do it on the command line):
for i in `cat $1` ; do
[ -f $i ] && echo File $i exists || echo File $i does not exist
done
the backticks around the cat execute the command and substitute the output into the loop.

Converting relative path into absolute path?

I'm not sure if these paths are duplicates. Given the relative path, how do I determine absolute path using a shell script?
Example:
relative path: /x/y/../../a/b/z/../c/d
absolute path: /a/b/c/d
The most reliable method I've come across in unix is readlink -f:
$ readlink -f /x/y/../../a/b/z/../c/d
/a/b/c/d
A couple caveats:
This also has the side-effect of resolving all symlinks. This may or may not be desirable, but usually is.
readlink will give a blank result if you reference a non-existant directory. If you want to support non-existant paths, use readlink -m instead. Unfortunately this option doesn't exist on versions of readlink released before ~2005.
From this source comes:
#!/bin/bash
# Assume parameter passed in is a relative path to a directory.
# For brevity, we won't do argument type or length checking.
ABS_PATH=`cd "$1"; pwd` # double quotes for paths that contain spaces etc...
echo "Absolute path: $ABS_PATH"
You can also do a Perl one-liner, e.g. using Cwd::abs_path
Using bash
# Directory
relative_dir="folder/subfolder/"
absolute_dir="$( cd "$relative_dir" && pwd )"
# File
relative_file="folder/subfolder/file"
absolute_file="$( cd "${relative_file%/*}" && pwd )"/"${relative_file##*/}"
${relative_file%/*} is same result as dirname "$relative_file"
${relative_file##*/} is same result as basename "$relative_file"
Caveats: Does not resolve symbolic links (i.e. does not canonicalize path ) => May not differentiate all duplicates if you use symbolic links.
Using realpath
Command realpath does the job. An alternative is to use readlink -e (or readlink -f). However realpath is not often installed by default. If you cannot be sure realpath or readlink is present, you can substitute it using perl (see below).
Using perl
Steven Kramer proposes a shell alias if realpath is not available in your system:
$ alias realpath="perl -MCwd -e 'print Cwd::realpath(\$ARGV[0]),qq<\n>'"
$ realpath path/folder/file
/home/user/absolute/path/folder/file
or if you prefer using directly perl:
$ perl -MCwd -e 'print Cwd::realpath($ARGV[0]),qq<\n>' path/folder/file
/home/user/absolute/path/folder/file
This one-line perl command uses Cwd::realpath. There are in fact three perl functions. They take a single argument and return the absolute pathname. Below details are from documentation Perl5 > Core modules > Cwd.
abs_path() uses the same algorithm as getcwd(). Symbolic links and relative-path components (. and ..) are resolved to return the canonical pathname, just like realpath.
use Cwd 'abs_path';
my $abs_path = abs_path($file);
realpath() is a synonym for abs_path()
use Cwd 'realpath';
my $abs_path = realpath($file);
fast_abs_path() is a more dangerous, but potentially faster version of abs_path()
use Cwd 'fast_abs_path';
my $abs_path = fast_abs_path($file);
These functions are exported only on request => therefore use Cwd to avoid the "Undefined subroutine" error as pointed out by arielf. If you want to import all these three functions, you can use a single use Cwd line:
use Cwd qw(abs_path realpath fast_abs_path);
Take a look at 'realpath'.
$ realpath
usage: realpath [-q] path [...]
$ realpath ../../../../../
/data/home
Since I've run into this many times over the years, and this time around I needed a pure bash portable version that I could use on OSX and linux, I went ahead and wrote one:
The living version lives here:
https://github.com/keen99/shell-functions/tree/master/resolve_path
but for the sake of SO, here's the current version (I feel it's well tested..but I'm open to feedback!)
Might not be difficult to make it work for plain bourne shell (sh), but I didn't try...I like $FUNCNAME too much. :)
#!/bin/bash
resolve_path() {
#I'm bash only, please!
# usage: resolve_path <a file or directory>
# follows symlinks and relative paths, returns a full real path
#
local owd="$PWD"
#echo "$FUNCNAME for $1" >&2
local opath="$1"
local npath=""
local obase=$(basename "$opath")
local odir=$(dirname "$opath")
if [[ -L "$opath" ]]
then
#it's a link.
#file or directory, we want to cd into it's dir
cd $odir
#then extract where the link points.
npath=$(readlink "$obase")
#have to -L BEFORE we -f, because -f includes -L :(
if [[ -L $npath ]]
then
#the link points to another symlink, so go follow that.
resolve_path "$npath"
#and finish out early, we're done.
return $?
#done
elif [[ -f $npath ]]
#the link points to a file.
then
#get the dir for the new file
nbase=$(basename $npath)
npath=$(dirname $npath)
cd "$npath"
ndir=$(pwd -P)
retval=0
#done
elif [[ -d $npath ]]
then
#the link points to a directory.
cd "$npath"
ndir=$(pwd -P)
retval=0
#done
else
echo "$FUNCNAME: ERROR: unknown condition inside link!!" >&2
echo "opath [[ $opath ]]" >&2
echo "npath [[ $npath ]]" >&2
return 1
fi
else
if ! [[ -e "$opath" ]]
then
echo "$FUNCNAME: $opath: No such file or directory" >&2
return 1
#and break early
elif [[ -d "$opath" ]]
then
cd "$opath"
ndir=$(pwd -P)
retval=0
#done
elif [[ -f "$opath" ]]
then
cd $odir
ndir=$(pwd -P)
nbase=$(basename "$opath")
retval=0
#done
else
echo "$FUNCNAME: ERROR: unknown condition outside link!!" >&2
echo "opath [[ $opath ]]" >&2
return 1
fi
fi
#now assemble our output
echo -n "$ndir"
if [[ "x${nbase:=}" != "x" ]]
then
echo "/$nbase"
else
echo
fi
#now return to where we were
cd "$owd"
return $retval
}
here's a classic example, thanks to brew:
%% ls -l `which mvn`
lrwxr-xr-x 1 draistrick 502 29 Dec 17 10:50 /usr/local/bin/mvn# -> ../Cellar/maven/3.2.3/bin/mvn
use this function and it will return the -real- path:
%% cat test.sh
#!/bin/bash
. resolve_path.inc
echo
echo "relative symlinked path:"
which mvn
echo
echo "and the real path:"
resolve_path `which mvn`
%% test.sh
relative symlinked path:
/usr/local/bin/mvn
and the real path:
/usr/local/Cellar/maven/3.2.3/libexec/bin/mvn
I wanted to use realpath but it is not available on my system (macOS), so I came up with this script:
#!/bin/sh
# NAME
# absolute_path.sh -- convert relative path into absolute path
#
# SYNOPSYS
# absolute_path.sh ../relative/path/to/file
echo "$(cd $(dirname $1); pwd)/$(basename $1)"
Example:
./absolute_path.sh ../styles/academy-of-management-review.csl
/Users/doej/GitHub/styles/academy-of-management-review.csl
May be this helps:
$path = "~user/dir/../file"
$resolvedPath = glob($path); # (To resolve paths with '~')
# Since glob does not resolve relative path, we use abs_path
$absPath = abs_path($path);