How can a Bash command remember and toggle a setting? - bash

I want to have a launcher that runs a Bash commands that toggle a setting; switching the setting one way requires one command and switching it the other way requires another command. If there is no easy way to query the system to find out the status of that setting, how should Bash remember the status of the setting so that it can run the appropriate command?
An obvious solution would be to save the status as files and then check for the existence of those files to determine the appropriate command to run, but is there some neater way, perhaps one that would use volatile memory?
Here's an attempt at a toggle script using temporary files:
#!/bin/bash
main(){
settingOn="/tmp/red_on.txt"
settingOff="/tmp/red_off.txt"
if [[ ! -e "${settingOff}" ]] && [[ ! -e "${settingOn}" ]]; then
echo "no prior use detected -- creating default off"
touch "${settingOff}"
fi
if [ -f "${settingOff}" ]; then
echo "switch on"
redshift -o -t 1000:1000 -l 0.0:0.0
rm -f "${settingOff}"
touch "${settingOn}"
elif [ -f "${settingOn}" ]; then
echo "switch off"
redshift -x
rm -f "${settingOn}"
touch "${settingOff}"
fi
}
main

Related

How to remove a single command from bash autocomplete

How do I remove a single "command" from Bash's auto complete command suggestions? I'm asking about the very first argument, the command, in auto complete, not asking "How to disable bash autocomplete for the arguments of a specific command"
For example, if I have the command ls and the system path also finds ls_not_the_one_I_want_ever, and I type ls and then press tab, I want a way to have removed ls_not_the_one_I_want_ever from every being a viable option.
I think this might be related to the compgen -c list, as this seems to be the list of commands available.
Background: WSL on Windows is putting all the .dll files on my path, in addition to the .exe files that should be there, and so I have many dlls I would like to remove in my bash environment, but I'm unsure how to proceed.
Bash 5.0's complete command added a new -I option for this.
According to man bash —
complete -pr [-DEI] [name ...]
[...] The -I option indicates that other supplied options and actions should apply to completion on the initial non-assignment word on the line, or after a command delimiter such as ; or |, which is usually command name completion. [...]
Example:
function _comp_commands()
{
local cur=$2
if [[ $cur == ls* ]]; then
COMPREPLY=( $(compgen -c "$cur" | grep -v ls_not_wanted) )
fi
}
complete -o bashdefault -I -F _comp_commands
Using #pynexj's answer, I came up with the following example that seems to work well enough:
if [ "${BASH_VERSINFO[0]}" -ge "5" ]; then
function _custom_initial_word_complete()
{
if [ "${2-}" != "" ]; then
if [ "${2::2}" == "ls" ]; then
COMPREPLY=($(compgen -c "${2}" | \grep -v ls_not_the_one_I_want_ever))
else
COMPREPLY=($(compgen -c "${2}"))
fi
fi
}
complete -I -F _custom_initial_word_complete
fi

Stop watching a folder for changes if a file exists (bash)

I'm using fswatch to keep track of changes to a directory, but would like this process to stop if a certain file exists (with a wildcard). This certain file is created in an alternative directory (not in the tracked directory) by another process (which is generating changes that need to be tracked).
Here's what I tried to do:
while [[ $(shopt -s nullglob; set -- "${file_to_check}"; echo $#) -eq 1 ]]; do
fswatch "${path_to_the_tracked_directory}"
done && echo "Done"
However, this script does not terminate after ${file_to_check} appears.
The complex bit in the condition is to take care of wildcards as per: Bash check if file exists with double bracket test and wildcards
EDIT:
The complex bit can be simplified to:
while [ $(set -- "${path_to_the_file_to_check}"${file_to_check_with_wildcards}; echo $#) -eq 0 ]; do
fswatch "${path_to_the_tracked_directory}"
done && echo "Done"
One solution is to use -1/--one-event option (https://github.com/emcrisostomo/fswatch/wiki/How-to-Use-fswatch).
The code then looks as:
while [ $(set -- "${path_to_the_file_to_check}"${file_to_check_with_wildcards}; echo $#) -eq 0 ]; do
fswatch -1 "${path_to_the_tracked_directory}"
done && echo "Done"

Bash - A way to watch files for changes?

My current solution is to use the python library watchdog and the bash snippet (originally taken from here).
watchmedo shell-command client/js/src/templates/ proto/ --recursive \
--patterns="*.soy;*.proto" \
--command="echo \"WATCHMEDO file changed - rebuilding\"; make genfiles;"
Basically I'm watching a few template files, and then running make genfiles automatically if one of them changes.
I'm wondering if there's a way to do this in pure bash? I'd rather not have all my devs have to depend on that Python library.
I'm on OSX.
#!/bin/bash
watched_files=$# # pass watched files as cmd line arguments
if [ -z "$watched_files" ]; then
echo "Nothing to watch, abort"
exit
else
echo "watching: $watched_files"
fi
previous_checksum="dummy"
while [ 1 ]; do
checksum=$(md5 $watched_files | md5) # I use Mac so I have `md5`, in linux it's `md5sum`
if [ "$checksum" != "$previous_checksum" ]; then
echo "None shall pass!" # do your stuff here
fi
previous_checksum="$checksum"
sleep 1
done
This is a nice cli FAM client: http://fileschanged.sourceforge.net/

Delete a directory only if it exists using a shell script

I have a shell (ksh) script. I want to determine whether a certain directory is present in /tmp, and if it is present then I have to delete it. My script is:
test
#!/usr/bin/ksh
# what should I write here?
if [[ -f /tmp/dir.lock ]]; then
echo "Removing Lock"
rm -rf /tmp/dir.lock
fi
How can I proceed? I'm not getting the wanted result: the directory is not removed when I execute the script and I'm not getting Removing Lock output on my screen.
I checked manually and the lock file is present in the location.
The lock file is created with set MUTEX_LOCK "/tmp/dir.lock" by a TCL program.
In addition to -f versus -d note that [[ ]] is not POSIX, while [ ] is. Any string or path you use more than once should be in a variable to avoid typing errors, especially when you use rm -rf which deletes with extreme prejudice. The most portable solution would be
DIR=/tmp/dir.lock
if [ -d "$DIR" ]; then
printf '%s\n' "Removing Lock ($DIR)"
rm -rf "$DIR"
fi
For directory check, you should use -d:
if [[ -d /tmp/dir.lock ]]; then
echo "Removing Lock"
rm -rf /tmp/dir.lock
fi

inotify with network server

I was thinking this was going to be rather easy, but it's turn out not to be.
I have a mounted network server that has inotifywatch running on it watching a folder. I was wanting to have this set up so that anyone on the network with access to this server could drop a file into the watched folder and have it execute a script. Unfortunately it doesn't seem to trigger unless the file was moved on the server itself.
Is there any way to make inotify watch a folder in a way that if any file, from anywhere, triggers the inotify event? Or should I look into something else?
For reference, here is what I'm using in a shell script:
inotifywait -m --format '%f' -e moved_to "/mnt/server/folder/" |
while read NAME
do
echo $NAME
done
I ended up setting up rsynch in a cron job to copy over the folder from the network every few minutes, then use inotifywatch to pick up new files from there.
inotifywait workaround for mounted drives :/
saveloop2 ./03_packages.js "clear" "node ./03_packages.js"
#!/usr/bin/env bash
if [[ "$#" -lt 2 ]]; then
echo -e "\e[91;48;5;52musage: saveloop <watchfile> <execution script> [another] [another] ✗✗✗\e[0m\n"
echo -e "example: saveloop .gitalias \"foo param1 param2\" [\"command2\"] [\"command2\"]"
echo -e "Note: each command with its params to be in quotes."
exit
fi
while :
do
oldTimestamp=$timestamp
timestamp=$(stat -c %Y $1)
if [[ $oldTimestamp != $timestamp ]]; then
$2
$3
$4
fi
sleep 1
done

Resources