Linux - Put a custom script in custom load state in SDDM - linux

appreciate if you can help. I'm trying to move my Google Chrome cache to RAM-disk, and I doing it following the instruction. It says, I need to put script in my display manager config this way:
GDM (Gnome Display Manager) or MDM - /etc/mdm/PostSession/Default
add "/etc/init.d/ramdisksaverestore stop" before line "exit 0"
LightDM - /etc/lightdm/lightdm.conf
add "session-cleanup-script = /etc/init.d/ramdisksaverestore stop" in the end of "[SeatDefaults]" section.
But I use Linux Mint 18 KDE (Ubuntu-based) with SDDM, and have no idea where should I put my script (I mean both file and line of it). Any help?
Maybe I should put that in the /usr/share/sddm/scripts/Xsession but where and how?
#! /bin/sh
# Xsession - run as user
# This file is extracted from kde-workspace (kdm/kfrontend/genkdmconf.c)
# Copyright (C) 2001-2005 Oswald Buddenhagen <ossi#kde.org>
session=$1
# Note that the respective logout scripts are not sourced.
case $SHELL in
*/bash)
[ -z "$BASH" ] && exec $SHELL $0 "$#"
set +o posix
[ -f /etc/profile ] && . /etc/profile
if [ -f $HOME/.bash_profile ]; then
. $HOME/.bash_profile
elif [ -f $HOME/.bash_login ]; then
. $HOME/.bash_login
elif [ -f $HOME/.profile ]; then
. $HOME/.profile
fi
;;
*/zsh)
[ -z "$ZSH_NAME" ] && exec $SHELL $0 "$#"
[ -d /etc/zsh ] && zdir=/etc/zsh || zdir=/etc
zhome=${ZDOTDIR:-$HOME}
# zshenv is always sourced automatically.
[ -f $zdir/zprofile ] && . $zdir/zprofile
[ -f $zhome/.zprofile ] && . $zhome/.zprofile
[ -f $zdir/zlogin ] && . $zdir/zlogin
[ -f $zhome/.zlogin ] && . $zhome/.zlogin
emulate -R sh
;;
*/csh|*/tcsh)
# [t]cshrc is always sourced automatically.
# Note that sourcing csh.login after .cshrc is non-standard.
xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX`
$SHELL -c "if (-f /etc/csh.login) source /etc/csh.login; if (-f ~/.login) source ~/.login; /bin/sh -c 'export -p' >! $xsess_tmp"
. $xsess_tmp
rm -f $xsess_tmp
;;
*) # Plain sh, ksh, and anything we do not know.
[ -f /etc/profile ] && . /etc/profile
[ -f $HOME/.profile ] && . $HOME/.profile
;;
esac
[ -f /etc/xprofile ] && . /etc/xprofile
[ -f $HOME/.xprofile ] && . $HOME/.xprofile
# run all system xinitrc shell scripts.
if [ -d /etc/X11/xinit/xinitrc.d ]; then
for i in /etc/X11/xinit/xinitrc.d/* ; do
if [ -x "$i" ]; then
. "$i"
fi
done
fi
# Load Xsession scripts
# OPTIONFILE, USERXSESSION, USERXSESSIONRC and ALTUSERXSESSION are required
# by the scripts to work
xsessionddir="/etc/X11/Xsession.d"
OPTIONFILE=/etc/X11/Xsession.options
USERXSESSION=$HOME/.xsession
USERXSESSIONRC=$HOME/.xsessionrc
ALTUSERXSESSION=$HOME/.Xsession
if [ -d "$xsessionddir" ]; then
for i in `ls $xsessionddir`; do
script="$xsessionddir/$i"
echo "Loading X session script $script"
if [ -r "$script" -a -f "$script" ] && expr "$i" : '^[[:alnum:]_-]\+$' > /dev/null; then
. "$script"
fi
done
fi
if [ -d /etc/X11/Xresources ]; then
for i in /etc/X11/Xresources/*; do
[ -f $i ] && xrdb -merge $i
done
elif [ -f /etc/X11/Xresources ]; then
xrdb -merge /etc/X11/Xresources
fi
[ -f $HOME/.Xresources ] && xrdb -merge $HOME/.Xresources
case $session in
"")
exec xmessage -center -buttons OK:0 -default OK "Sorry, $DESKTOP_SESSION is no valid session."
;;
*)
eval exec "$session"
;;
esac
exec xmessage -center -buttons OK:0 -default OK "Sorry, cannot execute $session. Check $DESKTOP_SESSION.desktop."

well, it turns out the best decision is to use PSD ( https://wiki.archlinux.org/index.php/profile-sync-daemon) and do not worry about anything, it will solve all the problems by itself.

Related

InfluxDB refusing connections

I am working with influxDB. My requirement is that i have to edit init-influxdb.sh file and add a small script at the end of this file. My init-influxdb.sh file code is:
#!/bin/bash
set -e
AUTH_ENABLED="$INFLUXDB_HTTP_AUTH_ENABLED"
INIT_USERS=$([ ! -z "$AUTH_ENABLED" ] && [ ! -z "$INFLUXDB_ADMIN_USER" ] && echo 1 || echo)
if [ -z "$INFLUXDB_META_DIR" ]; then
META_DIR="/var/lib/influxdb/meta"
else
META_DIR="$INFLUXDB_META_DIR"
fi
if ( [ ! -z "$INIT_USERS" ] || [ ! -z "$INFLUXDB_DB" ] || [ "$(ls -A /docker-entrypoint-initdb.d 2> /dev/null)" ] ) && [ ! "$(ls -d "$META_DIR" 2>/dev/null)" ]; then
INIT_QUERY=""
CREATE_DB_QUERY="CREATE DATABASE $INFLUXDB_DB"
if [ ! -z "$INIT_USERS" ]; then
if [ -z "$INFLUXDB_ADMIN_PASSWORD" ]; then
INFLUXDB_ADMIN_PASSWORD="$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c32;echo;)"
echo "INFLUXDB_ADMIN_PASSWORD:$INFLUXDB_ADMIN_PASSWORD"
fi
INIT_QUERY="CREATE USER \"$INFLUXDB_ADMIN_USER\" WITH PASSWORD '$INFLUXDB_ADMIN_PASSWORD' WITH ALL PRIVILEGES"
elif [ ! -z "$INFLUXDB_DB" ]; then
INIT_QUERY="$CREATE_DB_QUERY"
else
INIT_QUERY="SHOW DATABASES"
fi
INFLUXDB_INIT_PORT="8086"
INFLUXDB_HTTP_BIND_ADDRESS=127.0.0.1:$INFLUXDB_INIT_PORT INFLUXDB_HTTP_HTTPS_ENABLED=false influxd "$#" &
pid="$!"
INFLUX_CMD="influx -host 127.0.0.1 -port $INFLUXDB_INIT_PORT -execute "
for i in {30..0}; do
if $INFLUX_CMD "$INIT_QUERY" &> /dev/null; then
break
fi
echo 'influxdb init process in progress...'
sleep 1
done
if [ "$i" = 0 ]; then
echo >&2 'influxdb init process failed.'
exit 1
fi
if [ ! -z "$INIT_USERS" ]; then
INFLUX_CMD="influx -host 127.0.0.1 -port $INFLUXDB_INIT_PORT -username ${INFLUXDB_ADMIN_USER} -password ${INFLUXDB_ADMIN_PASSWORD} -execute "
if [ ! -z "$INFLUXDB_DB" ]; then
$INFLUX_CMD "$CREATE_DB_QUERY"
fi
if [ ! -z "$INFLUXDB_USER" ] && [ -z "$INFLUXDB_USER_PASSWORD" ]; then
INFLUXDB_USER_PASSWORD="$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c32;echo;)"
echo "INFLUXDB_USER_PASSWORD:$INFLUXDB_USER_PASSWORD"
fi
if [ ! -z "$INFLUXDB_USER" ]; then
$INFLUX_CMD "CREATE USER \"$INFLUXDB_USER\" WITH PASSWORD '$INFLUXDB_USER_PASSWORD'"
$INFLUX_CMD "REVOKE ALL PRIVILEGES FROM \"$INFLUXDB_USER\""
if [ ! -z "$INFLUXDB_DB" ]; then
$INFLUX_CMD "GRANT ALL ON \"$INFLUXDB_DB\" TO \"$INFLUXDB_USER\""
fi
fi
if [ ! -z "$INFLUXDB_WRITE_USER" ] && [ -z "$INFLUXDB_WRITE_USER_PASSWORD" ]; then
INFLUXDB_WRITE_USER_PASSWORD="$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c32;echo;)"
echo "INFLUXDB_WRITE_USER_PASSWORD:$INFLUXDB_WRITE_USER_PASSWORD"
fi
if [ ! -z "$INFLUXDB_WRITE_USER" ]; then
$INFLUX_CMD "CREATE USER \"$INFLUXDB_WRITE_USER\" WITH PASSWORD '$INFLUXDB_WRITE_USER_PASSWORD'"
$INFLUX_CMD "REVOKE ALL PRIVILEGES FROM \"$INFLUXDB_WRITE_USER\""
if [ ! -z "$INFLUXDB_DB" ]; then
$INFLUX_CMD "GRANT WRITE ON \"$INFLUXDB_DB\" TO \"$INFLUXDB_WRITE_USER\""
fi
fi
if [ ! -z "$INFLUXDB_READ_USER" ] && [ -z "$INFLUXDB_READ_USER_PASSWORD" ]; then
INFLUXDB_READ_USER_PASSWORD="$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c32;echo;)"
echo "INFLUXDB_READ_USER_PASSWORD:$INFLUXDB_READ_USER_PASSWORD"
fi
if [ ! -z "$INFLUXDB_READ_USER" ]; then
$INFLUX_CMD "CREATE USER \"$INFLUXDB_READ_USER\" WITH PASSWORD '$INFLUXDB_READ_USER_PASSWORD'"
$INFLUX_CMD "REVOKE ALL PRIVILEGES FROM \"$INFLUXDB_READ_USER\""
if [ ! -z "$INFLUXDB_DB" ]; then
$INFLUX_CMD "GRANT READ ON \"$INFLUXDB_DB\" TO \"$INFLUXDB_READ_USER\""
fi
fi
fi
for f in /docker-entrypoint-initdb.d/*; do
case "$f" in
*.sh) echo "$0: running $f"; . "$f" ;;
*.iql) echo "$0: running $f"; $INFLUX_CMD "$(cat ""$f"")"; echo ;;
*) echo "$0: ignoring $f" ;;
esac
echo
done
if ! kill -s TERM "$pid" || ! wait "$pid"; then
echo >&2 'influxdb init process failed. (Could not stop influxdb)'
exit 1
fi
fi
# My custom script starts here
INIT_QUERY="CREATE USER \"$INFLUXDB_ADMIN_USER\" WITH PASSWORD '$INFLUXDB_ADMIN_PASSWORD' WITH ALL PRIVILEGES"
INFLUXDB_INIT_PORT="8086"
INFLUX_CMD="influx -host 127.0.0.1 -port $INFLUXDB_INIT_PORT -username admin -password admin -execute "
echo 'Creating admin user of influx db'
$INFLUX_CMD "$INIT_QUERY"
echo 'Admin user of influx db created successfully'
echo 'Enabling authentication on influxdb server'
if [ -z "$AUTH_ENABLED" ]; then
AUTH_ENABLED="$(grep -iE '^\s*auth-enabled\s*=\s*true' /etc/influxdb/influxdb.conf | grep -io 'true' | cat)"
else
AUTH_ENABLED="$(echo "$INFLUXDB_HTTP_AUTH_ENABLED" | grep -io 'true' | cat)"
fi
Issue is that, after printing "Creating admin user of influx db" i get the error "Failed to connect to http://localhost:8086: Get http://localhost:8086/ping: dial tcp [::1]:8086: getsockopt
Please check your connection settings and ensure 'influxd' is running."
What can be the possible issue here ? There isn't any error on any other query. Why i am getting this error. I am stuck, any help would be much appreciated.
My DockerFile is:
FROM influxdb:1.7.6
ENV INFLUXDB_ADMIN_USER="admin"
ENV INFLUXDB_ADMIN_PASSWORD="admin"
ENV INFLUXDB_HTTP_AUTH_ENABLED=true
ENV TZ=America/Los_Angeles
RUN chmod +x entrypoint.sh
RUN chmod 777 /init-influxdb.sh
COPY init-influxdb.sh /
RUN chmod 777 /init-influxdb.sh
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
I think the problem is that the DB is not yet started when you try to run the statement.
Checking the rest of the script you can see their solution to the problem:
for i in {30..0}; do
if $INFLUX_CMD "$INIT_QUERY" &> /dev/null; then
break
fi
echo 'influxdb init process in progress...'
sleep 1
done
if [ "$i" = 0 ]; then
echo >&2 'influxdb init process failed.'
exit 1
fi
I think your setup does not involve the execution of that portion of the script though.

Create menu from all users with /home/ directory in bash

I am trying to write a bash script to remove cookies and cache from installed browsers on shared Ubuntu machines. The problem I am facing is in creating a menu where you can select either ALL users or individual users.
I am trying to create a main menu that calls either of the 2 functions (a work in progress) to perform the tasks (I have commented out the commands to run for the meantime).
#!/bin/bash
# Remove Browser cache from Ubuntu 16.04 or Ubuntu 18.04
# Check running as root/sudo
if [ "$EUID" -ne 0 ] ;then
echo -e "Please run with;\nsudo $0"
exit
fi
# Enable extended globbing for the +(...) pattern
shopt -s extglob
## Check Ubuntu version
VERSION=$(lsb_release -d | awk -F":" '/Description/ {print $2}')
if [[ "$VERSION" = *"Ubuntu 18.04"* ]]; then
HOME_DIR="/home/ANT.DOMAIN.COM"
else
[[ "$VERSION" = *"Ubuntu 16.04"* ]]
HOME_DIR="/home/local/ANT"
fi
# Set Colours
RED='\033[1;31m'
YELLOW='\033[1;33m'
GREEN='\033[1;32m'
NC='\033[0m' # No Color
## Clear Browser Cache for ALL Users
clear_cache_all () {
mapfile -t PROFILES < <(find "$HOME_DIR" -mindepth 1 -maxdepth 1 -type d)
for PRO in "${PROFILES[#]}"
do
# Check FireFox installed
dpkg -s firefox &> /dev/null
if [ $? -eq 0 ]; then
#rm -rf "$PRO"/.mozilla/firefox/*.default/*.sqlite "$PRO"/.mozilla/firefox/*default/sessionstore.js
#rm -rf "$PRO"/.cache/mozilla/firefox/*.default/*
echo -e "FireFox Cookies & Cache Cleared for user ${GREEN}$USERNAME${NC}"
else
echo -e "${YELLOW}FireFox Not Installed...moving on${NC}"
fi
# Check Chromium installed
dpkg -s chromium-browser &> /dev/null
if [ $? -eq 0 ]; then
#rm -rf "$PRO"/.config/chromium/Default/
#rm -rf "$PRO"/.cache/chromium
echo -e "Chromium Cookies & Cache Cleared for user ${GREEN}$USERNAME${NC}"
else
echo -e "${YELLOW}Chromium Not Installed...moving on${NC}"
fi
# Check Chrome installed
dpkg -s google-chrome-stable &> /dev/null
if [ $? -eq 0 ]; then
#rm -rf "$PRO"/.config/google-chrome/Default/
#rm -rf "$PRO"/.cache/google-chrome
echo -e "Google Chrome Cookies & Cache Cleared for user ${GREEN}$USERNAME${NC}"
else
echo -e "${YELLOW}Google Chrome Not Installed...moving on${NC}"
fi
done
}
## Clear Cache for Individual Users
clear_cache_user () {
echo "stuff!"
}
# main menu function
main_menu () {
clear
if [ -d "$HOME_DIR" ]
then
mapfile -t USERS < <(find "$HOME_DIR" -mindepth 1 -maxdepth 1 -type d)
# Get basename for users
USERNAME="${USERS[#]##*/}"
string="#(${USERNAME[0]}"
for((i=1;i<${#USERNAME[#]};i++))
do
string+="|${USERNAME[$i]}"
done
string+=")"
select NAME in "Clear ALL" "${USERNAME[#]}" "Quit"
do
case $NAME in
"Clear ALL")
# Call clear_cache_all Function
clear_cache_all
exit
;;
$string)
# Call clear_cache_user Function
clear_cache_user
;;
"Quit")
exit
;;
*)
echo "Invalid option, please try again";;
esac
done
else
echo -e "${RED}Error: Cannot find home directories...exiting${NC}"
fi
}
### SCRIPT COMMANDS ###
main_menu
Ok, so I can think of two options for your problem. I'll try to follow the names of your variables.
As I can see in your code, you have already put in the variable "string" all the usernames, so my first idea is to use a read and a simple if:
read -P "Insert ALL for all users, the Username for a single user, or Quit to exit: " NAME
if [ $NAME = "ALL" ]
then
clear_cache_all
exit
elif [ $NAME = "Quit" ]
then
echo "Bye!"
exit
else
for i in "${string[#]}"
do
if [ "$i" == "$NAME" ] ; then
clear_cache_user($NAME) #Guessing you'll pass the username as a variable to the function
exit
fi
done
echo "Invalid option, please try again"
fi
The other option is to use the case statement, as you were using. The problem is that case doesn't work easy with arrays, so while it's "case / in", it doesn't mean it's checking if the variable is an element of the array. In case you are forced to use case (or are in love with it), check this two links for some solutions: this one and this one.
Hope this helps! Good luck!

Editing file with vim without typing path (similar to autojump)

A few months back, I installed a utility on my mac so that instead of typing something like this:
vim /type/path/to/the/file
I could just type:
v file
9 times out of 10 it would guess the right file based on the past history, similar to the way autojump works. And instead of typing in vim I can just type the letter v.
I can't remember how I set this up though. It still works on my mac but I don't see anything in my .bash_profile that shows how I did that.
I'm trying to get this to work on my linux box.
This can be found here
https://github.com/rupa/v/blob/master/v
it should work in Linux too. It is a bash script that uses the viminfo
history file to fill in partial strings.
It can be installed on macOS with brew install v
Ah! I found the command with which. Here is the magical script. I can't determine where I got it.
#!/usr/bin/env bash
[ "$vim" ] || vim=vim
[ $viminfo ] || viminfo=~/.viminfo
usage="$(basename $0) [-a] [-l] [-[0-9]] [--debug] [--help] [regexes]"
[ $1 ] || list=1
fnd=()
for x; do case $x in
-a) deleted=1;;
-l) list=1;;
-[1-9]) edit=${x:1}; shift;;
--help) echo $usage; exit;;
--debug) vim=echo;;
--) shift; fnd+=("$#"); break;;
*) fnd+=("$x");;
esac; shift; done
set -- "${fnd[#]}"
[ -f "$1" ] && {
$vim "$1"
exit
}
while IFS=" " read line; do
[ "${line:0:1}" = ">" ] || continue
fl=${line:2}
[ -f "${fl/\~/$HOME/}" -o "$deleted" ] || continue
match=1
for x; do
[[ "$fl" =~ $x ]] || match=
done
[ "$match" ] || continue
i=$((i+1))
files[$i]="$fl"
done < "$viminfo"
if [ "$edit" ]; then
resp=${files[$edit]}
elif [ "$i" = 1 -o "$list" = "" ]; then
resp=${files[1]}
elif [ "$i" ]; then
while [ $i -gt 0 ]; do
echo -e "$i\t${files[$i]}"
i=$((i-1))
done
read -p '> ' CHOICE
resp=${files[$CHOICE]}
fi
[ "$resp" ] || exit
$vim "${resp/\~/$HOME}"

How to autocomplete a bash commandline with file paths from a specific directory without displaying this directory?

You can find a related question here: How to autocomplete a bash commandline with file paths?
Context
I am creating a shell program which is a command line tool. I want to create my own auto-completion for this tool.
For the options --unit-test and -t, I want to auto-complete on file paths from a particular directory which I can get running my_app --directory.
e.G.
Run:
user#computer:~$ my_app --install [TAB][TAB]
would do:
Public/ bin/ Desktop/
Documents/ Music/ Downloads/
user#computer:~$ my_app --install
(display the current directory)
Run:
user#computer:~$ my_app --unit-tests [TAB][TAB]
would do:
folder/ folder2/ folder3/
.hidden_file file.extension file2.extension
user#computer:~$ my_app --unit-tests
(display suggestions for specific directory without complete with it)
my_app_autocomplete file
__my_app_autocomplete()
{
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="--help -h --install -i --run -r --rebuild -rb --show-running-containers -ps --stop -s --remove -rm --logs -l --bash -b --sass -css --unit-tests -t"
containers="nginx php mysql mongo node"
sass="watch"
# By default, autocomplete with options
if [[ ${prev} == my_app ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
# By default, autocomplete with options
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
# For --install and -i options, autocomplete with folder
if [ ${prev} == --install ] || [ ${prev} == -i ] ; then
COMPREPLY=( $(compgen -d -- ${cur}) )
return 0
fi
# For --stop --remove --logs and --bash, autocomplete with containers
if [ ${prev} == --stop ] || [ ${prev} == -s ] || [ ${prev} == --remove ] || [ ${prev} == -rm ] || [ ${prev} == --logs ] || [ ${prev} == -l ] || [ ${prev} == --bash ] || [ ${prev} == -b ] ; then
COMPREPLY=( $(compgen -W "${containers}" -- ${cur}) )
return 0
fi
# For --sass and -css, complete with sass options
if [ ${prev} == --sass ] || [ ${prev} == -css ] ; then
COMPREPLY=( $(compgen -W "${sass}" -- ${cur}) )
return 0
fi
# For --unit-tests and -t, complete from a specific folder
if [ ${prev} == --unit-tests ] || [ ${prev} == -t ] ; then
COMPREPLY=( $(compgen -d -- ${cur}) )
return 0
fi
}
complete -o filenames -F __my_app_autocomplete my_app
Problem
I can't find a way to do it. Do you have any ideas?
Investigations
Using a variable containing the specific directory
Suggested by #D'Arcy Nader
Adding at the beginning of my_app_autocomplete
_directory=/absolute/path/to/the/directory/
and then substitute the variable in the compgen command
# For --unit-tests and -t, complete with relative to my_app folder paths
if [ ${prev} == --unit-tests ] || [ ${prev} == -t ] ; then
COMPREPLY=( $(compgen -d -- "${_directory}") )
return 0
fi
Behavior:
Run
user#computer:~$ my_app --unit-tests [TAB][TAB]
do
user#computer:~$ my_app --unit-tests /absolute/path/to/the/directory/
It adds the path to the directory.
Run
user#computer:~$ my_app --unit-tests /absolute/path/to/the/directory/file.ext[TAB][TAB]
do
user#computer:~$ my_app --unit-tests /absolute/path/to/the/directory/
It removes the file.ext part.
Problems:
I don't want to add the specific path in the command line
It removes what I add after the specific directory instead of auto-complete it.
After a lot of try and error, I think I got a solution to your problem (which was my problem as well):
_complete_specific_path() {
# declare variables
local _item _COMPREPLY _old_pwd
# if we already are in the completed directory, skip this part
if [ "${PWD}" != "$1" ]; then
_old_pwd="${PWD}"
# magic here: go the specific directory!
pushd "$1" &>/dev/null || return
# init completion and run _filedir inside specific directory
_init_completion -s || return
_filedir
# iterate on original replies
for _item in "${COMPREPLY[#]}"; do
# this check seems complicated, but it handles the case
# where you have files/dirs of the same name
# in the current directory and in the completed one:
# we want only one "/" appended
if [ -d "${_item}" ] && [[ "${_item}" != */ ]] && [ ! -d "${_old_pwd}/${_item}" ]; then
# append a slash if directory
_COMPREPLY+=("${_item}/")
else
_COMPREPLY+=("${_item}")
fi
done
# popd as early as possible
popd &>/dev/null
# if only one reply and it is a directory, don't append a space
# (don't know why we must check for length == 2 though)
if [ ${#_COMPREPLY[#]} -eq 2 ]; then
if [[ "${_COMPREPLY}" == */ ]]; then
compopt -o nospace
fi
fi
# set the values in the right COMPREPLY variable
COMPREPLY=( "${_COMPREPLY[#]}" )
# clean up
unset _COMPREPLY
unset _item
else
# we already are in the completed directory, easy
_init_completion -s || return
_filedir
fi
}
I found this solution by looking at how cat is autocompleted. It uses the _longopt function, which in turn uses _filedir for arguments that are not options (not beginning with -).
Now you can declare a completion function for each directory you need, like:
_complete_git_home_path() {
_complete_specific_path "${GIT_HOME}"
}
And attach it to the right commands:
complete -F _complete_git_home_path cdrepo lsrepo rmrepo cdwiki pyinst
Or use it inside your own completion function, to trigger it for a specific option like --unit-test!
Improvements upon #pawamoy answer
when calling:
_init_completion -s || return
if _init_completion return a non null value the script will exit without having executed the popd command, this could leave you in the directory specified when calling pushd (but it even crashes my terminal!). I suggest doing this instead (see grouping commands for { } explanation)
_init_completion -s || { popd > /dev/null 2>&1; return; }
Also if you aim for portability, &> redirection is non portable since it is not part of the official POSIX shell spec (see this answer), you should use
> /dev/null 2>&1
instead of
&> /dev/null

Recycle Bin in Bash Script

I am trying to create a basic recycle bin concept in a VM using bash scripting. It will need to delete files that have been entered and place them into a directory that is created and save the path(origin) to a log file to be later used in a restore function.
I will start off with my delete/recycle code which I believe works just fine but seems kind of untidy/contains redundant code:
#!/bin/sh
if [ ! -d ~/recycle ]
then mkdir ~/recycle
fi
if [ ! -d ~/recycle/recycle_log ]
then mkdir ~/recycle/recycle_log
fi
if [ ! -d ~/recycle/recycle_bin ]
then mkdir ~/recycle/recycle_bin
fi
if [ -d ~/recycle ]
then
echo "$(readlink -f "$1")" >> "$HOME/recycle/recycle_log/log_file" && mv "$1" "$HOME/recycle/recycle_bin"
echo "$(readlink -f "$2")" >> "$HOME/recycle/recycle_log/log_file" && mv "$2" "$HOME/recycle/recycle_bin"
echo "$(readlink -f "$3")" >> "$HOME/recycle/recycle_log/log_file" && mv "$3" "$HOME/recycle/recycle_bin"
echo "$(readlink -f "$4")" >> "$HOME/recycle/recycle_log/log_file" && mv "$4" "$HOME/recycle/recycle_bin"
fi
#end
Thereafter what I have for my restore script is as follows:
#!/bin/sh
cd "$HOME/recycle/recycle_bin" || exit 1
mv -i "$(grep "$1" "$HOME/recycle/recycle_log")"
I imagine this is somewhat close to what I need to return any deleted file stored in the log/recycle bin to be restored to its origin but the error I am getting is:
mv: missing destination file operand after `'
Any thoughts on where I'm going wrong?
Try this:
recycle.sh
#!/bin/sh
set -e
check_dir() {
[ ! -d $1 ] || return 0
mkdir --parents $1
}
check_dir "${HOME}/recycle/recycle_bin"
touch "${HOME}/recycle/recycle_log"
for file in "$#"; do
echo "$(readlink -f "$file")" >> "${HOME}/recycle/recycle_log"
mv "$file" "${HOME}/recycle/recycle_bin"
done
#end
restore.sh
#!/bin/sh
set -e
cd "${HOME}/recycle/recycle_bin" || exit 1
for name in "$#"; do
file=$(grep "\/${name}\$" "${HOME}/recycle/recycle_log")
mv -i $name "$file"
sed -i "/\/${name}\$/ d" "${HOME}/recycle/recycle_log"
done
Some insights:
set -e: Abort on any error, to avoid some if's
$#: The array of arguments ($1, $2...)
[ ! -d $1 ] || return 0: Since we are using set -e, do not fail if the directory exists
grep "\/${name}\$" ...: Only matches the name at the end of the path
sed -i: sed in-place editing to remove the line

Resources