Bash - Read config files and make changes in this file - bash

i have config file like this for example:
# Blah blah, this is sample config file blah blah
# Something more blah blah
value1=YES
value2=something
#value3=boom
# Blah blah
valueN=4145
And i want to make script to read and edit config files like this. I thinking about make a menu with groups of config options, then after write an option console output will be like this:
Group of funny options (pick option to change value):
1. value1=YES
2. value2=something
3. [disabled]value3=boom
After picking 1 for exaple i can change value1 from YES to NO or disable and activate other (hash unhash) plus adding new variables to the end of file. Then in the end save all changes in this config file. Any tips what i need to use? Actually trying with read line + awk to skip # lines (with space), but still i have problem to get all this variables and making changes in config file. I will be grateful for your help.
Edit.
while read line
do
echo $line | awk '$1' != "#" && / / { print $1 $3 }'
done < config.conf
Thinking about this for now to read informations what i want. Plus i'm gonna use something like this to change values:
sed -c -i "s/("one" *= *).*/\1$two/" config.conf
I have completly no idea how i can get this variables to my script and use it like i write before. Actually i search for any tips, not someone who write this script for me. I'm beginner at linux scripting :V

I would recommend to abstain from such an, seemingly generic configuration program, because the comments might contain important informations about the current value and will be outdated, if the values change, while the comments don't.
Second problem is, that I would expect, if activating an entry is possible, deactivating it should be possible too. So now you have 2 options what to do with each value.
Third problem: In most cases, guessing a type by the value might work. YES seems to be a boolean, 47 an int, foobar a name - or is it a file? - but often a wider type is possible too, so YES can be just a string or a file, 47.3 might be valid where 47 is or might be not and so on.
However, for experimenting and trying things out, select and grep might be a start:
select line in $(grep "=" sample.conf) "write" "abort"
do
case $line in
"write") echo write; break ;;
"abort") echo abort; break ;;
'#'*=*) echo activate $line;;
*=[0-9]*) echo int value $line;;
*=YES|NO) echo boolean value $line;;
*) echo text value $line ;;
esac
done
Instead of 'echo intvalue $line' you would probably call a function "intconfigure" where only int values are accepted. For "write", you would write back to the file, but I omitted, conserving the comments without assignment and sorting them in again at the right position has to be done, which isn't trivial, given the opportunity to activate or deactivate comments.
But read up on the select command in shell and try it out and see how far you come.
If you think you have reached a usable solution, use this for all your configuration files privately and see, whether you prefer it over using a simple editor or not.

Related

Possible to get bash input while user is at prompt? (Essentially an event listener)

Old stuff:
Background:
- Ultimate goal is to put a script in my .bash_profile that warns me by changing text color if I'm typing a commit message and it gets too
long (yes I'm aware vim has something like this).
Progress:
- I found the read -n option which led me to write this:
while true; do
# This hits at the 53rd character
read -rn53 input
# I have commit aliased to gc so the if is just checking if I'm writing a commit
if [ "${input:0:2}" = "gc" ]; then
printf "\nMessage getting long"
fi
done
Question:
- However, running this takes the user out of the bash prompt. I need a way to do something like this while at a normal prompt. I can't find
information on anything like this. Does that mean it's not possible?
Or am I just going about it the wrong way?
New progress:
I found the bind -x option which led me to write this:
check_commit() {
if [ "${READLINE_LINE:0:13}" == 'git commit -m' ] && [ ${#READLINE_LINE} -gt 87 ]; then
echo "Commit is $((${#READLINE_LINE} - 87)) characters too long!"
fi
READLINE_LINE="$READLINE_LINE$1"
READLINE_POINT=$(($READLINE_POINT+1))
}
bind -x '"\"": check_commit "\""'
It listens for a double quote and if I'm writing a long commit message tells me how many characters I am over the limit. Also puts the character I typed into the current line since it is eaten by the bind.
New question:
Now I just need a way to put in a regex, character list or at least a variable instead of \" so I can listen on more keys (Yes, I'm aware bind -x probably wasn't intended to be used this way. I can check performance/footprint/stability myself). I tried "$char", "${char}", "$(char)" and a few other things, but none seem to work. What is the correct approach here ?
AFAIK, not possible in a sane way if you want this to happen during your normal prompt (when PROMPT_COMMAND and PS1 are evaluated). That would involved binding a custom compiled readline function for every insert-self and alike.
If you want this to happen in a script using prompt builtin, this is crudely possible with a loop of
read -e -i $(munge_buf $buf) -n $(buf_warn_len $buf) -p $(buf_warning $buf) buf
like commands. This will allow you to create munge_buf() to alter the currently typed text if needed, buf_warn_len() to calculate a new len to warn at (which may be very large if warning was already displayed), and buf_warn_msg() to derive a warning message based upon the buffer.

Bash Shell echo/printf how to format output the right way

My current snippet of code looks like this ...
#Location of network config files
nfds="/etc/sysconfig/network-scripts/"
#Standard prefer of network config files
fil="ifcfg-"
#Array variable that feeds "$nic"
cards= array loop built from "nic=$(ls /sys/class/net | grep en)"
#Set color for Divice labile
div="\033[38;5;39m"
#Set Fix format and colour info
fix="\033[38;5;118m"
#Set color for OK
ok="\033[38;5;28m"
#Clear All font and color info
ctf="\033[0m"
function currentCardDefRoute(){
defr=$(grep DEFROUTE $nfds$fil$cards | cut -d = -f 2)
if [[ $defr = "yes" ]] || [[ $defr = "no" ]]; then
echo -e " "$div$cards$ctf"'s current default route is\t"$div$defr$ctf"\t\t\t\t ["$ok"OK"$ctf"]"
$st
else
echo -e " "$div$cards$ctf"'s current default route is \t"$fix"Missing"$ctf"\t\t\t ["$fix"PLEASE FIX"$ctf"]"
$st
fi
}
I indent 1 space on all echo lines for readability and consistent formatting. Keeping output readable and easy to understand.
Im looking to us the "columns" option and make the output more dynamic and have the format consistent no matter the screen size or var result. I would love to also get rid of all the "\t"s in my code. I have tried printf to no success.
I googled a lot of different ways and not seen the specific answer Im looking for or a variation I can draw an answer from.
Thank you for your help.
btw. This is the first code I have ever written so go easy guys :)
You may want to try using the column utility. It's sole purpose is for formatting output into columns. That may be easier than trying to do the same thing with echo or printf.
If you have to use printf, you'll want to use a format specifier like "%25.25s". The first number is the "minimum field width", which (in this case) causes the output to be at least 25 characters wide. If the output is shorter, it's padded with whitespace. The second number indicates the maximum number of characters to print. When these two numbers are the same, it effectively says to print the string in a field that's exactly 25 characters wide. You can use this to force varying-length strings to take up the same amount of space on the screen.

Shell script Create case statements based on the file names mentioned in a text file

I have a use case for a shell script, where I want a user to select the files mentioned in a file. That choice can be used to install the selected package using other commands.
I have succeeded to create the list of required files in the directory.
I need help to populate them in the case choices where the user can select the file and do further actions.
I have used this to write the file for required filename:
dir=/root/vaibhav/install_package
> choices
for entry in "$search_dir"$dir/*.zip
do
echo "$entry" | cut -d "/" -f5 >> choices
done
the Output of the choice file is something like this:
content_abc.zip
content_xyz_test.zip
content_aacd_to_qa.zip
Now i want the script to read this file and convert it int choices where the user can do further action.
This is what i want:
case "<read_thefile"
1)<firstLine_from_file>
Do some actions
;;
2)<secondline_fromfile>
do some actions
;;
esac
Help me in this.
Basically you can do two things in your script:
generate the case (or select) switches from a template, write it to a file, and source that file. This could work, but if the input file (list of zips) is unreliable I'd not recommend it.
display the list with something like nl list_of_zips, then read an input from the user and based on that number you can pick the line from the list_of_zips, e.g.:
nl list_of_zips
# 1 OUTPUT_LINE1
# 2 OUTPUT_LINE2
### now read the choice
read choice
### and get the choice line from the file
zip_file_choosen=$(awk "NR==$choice {print ; exit }")
### and do what you want with $zip_file_choosen ...

How to parametrize verbosity of debug output (BASH)?

During the process of writing a script, I will use the command's output in varying ways, and to different degrees - in order to troubleshoot the task at hand.. For example, in this snippet, which reads an Application's icon resource and returns whether or not it has the typical .icns extension...
icns=`defaults read /$application/Contents/Info CFBundleIconFile`
if ! [[ $icns =~ ^(.*)(.icns)$ ]]; then
echo -e $icns "is NOT OK YOU IDIOT! **** You need to add .icns to "$icns"."
else
echo -e $icns "\t Homey, it's cool. That shits got its .icns, proper."
fi
Inevitably, as each bug is squashed, and the stdout starts relating more to the actual function vs. the debugging process, this feedback is usually either commented out, silenced, or deleted - for obvious reasons.
However, if one wanted to provide a simple option - either hardcoded, or passed as a parameter, to optionally show some, all, or none of "this kind" of message at runtime - what is the best way to provide that simple functionality? I am looking to basically duplicate the functionality of set -x but instead of a line-by rundown, it would only print the notifications that I had architected specificically.
It seems excessive to replace each and every echo with an if that checks for a debug=1|0, yet I've been unable to find a concise explanation of how to implement a getopts/getopt scheme (never can remember which one is the built-in), etc. in my own scripts. This little expression seemed promising, but there is very little documentation re: 2>$1 out there (although I'm sure this is key to this puzzle)
[ $DBG ] && DEBUG="" || DEBUG='</dev/null'
check_errs() {
# Parameter 1 is the return code Para. 2 is text to display on failure.
if [ "${1}" -ne "0" ]; then
echo "ERROR # ${1} : ${2}"
else
echo "SUCESSS "
fi }
Any concise and reusable tricks to this trade would be welcomed, and if I'm totally missing the boat, or if it was a snake, and it would be biting me - I apologize.
One easy trick is to simply replace your "logging" echo comamnd by a variable, i.e.
TRACE=:
if test "$1" = "-v"; then
TRACE=echo
shift
fi
$TRACE "You passed the -v option"
You can have any number of these for different types of messages if you wish so.
you may check a common open source trace library with support for bash.
http://sourceforge.net/projects/utalm/
https://github.com/ArnoCan/utalm
WKR
Arno-Can Uestuensoez

ksh string manipulation $##!?

So I have this spool file having this kind of content.
SQL> select file_name from dev_files;
FILE_NAME
------------------------------------------------------------------
file1.txt
file2.doc
file3.pdf
total.xls
4 rows selected.
SQL> spool off
I am writing a ksh script to load these files into a ftp server and update a log file. and I am stuck up bigtime. Here is the portion of my poor code after many tries.
dump="spoolfile.txt"
while read line;
do
if [[`expr match "$line" 'SQL'` !=3]] && [[`expr match "$line" 'FILE_NAME'` !=9]] && [[`expr match "$line" '---------'` !=9]]
then
ftp -inv $tgt_server <<EOT
quote user $uname
quote password $pword
mput $src_path/$line
quit
EOT
echo "sent $line" >> sent_files.log
fi
done < $dump
how do i ensure that "no rows selected" and say "4 rows selected." are not read? there can be any number instead of 4 corresponding to number of files. In the case of no files the spool file looks like this. the '.' is also missing.
SQL> select file_name from dev_files;
no rows selected
SQL> spool off
Here is a much more simple soltion: Instead of trying to find lines to ignore, mark the lines to process:
select concat("JHFDGFSH ",file_name) from dev_files
Now all you need to do is to ignore any line that doesn't start with JHFDGFSH.
[EDIT] If you can't do that: Start reading when you hit a line that's only ---- and stop at the first empty line. That should work unless Oracle starts to page.
And open an issue for upstream to tell them that their interface is brittle and that it will break eventually.
If that's OK, it's not your problem and not your fault when it breaks and production is down for a couple of days; just show the issue and say "see? You wanted it this way."
Also KSH is probably not the right tool; have a look at awk. This should work:
/^---+/,/^[ \t]*$/ { print; }
If you can't use awk, how about treating every line as a file name and ignore those which don't exist?
As long as no one creates a file named FILE_NAME, 4 rows selected. or SQL> spool off, you should be find as long as you make sure you properly quote all strings.
I agree with most of the comments about 'turn off headings' etc in the SQLPlus,
but this is Easy Peasy ;-)
while read line;
do
case ${line} in
-------* ) continue ;;
FILE_NAME ) continue ;;
SQL[>] ) continue ;;
*rows\ selected ) continue ;;
esac
# with enough case match targets, you can probably eliminate
# the if/fi completely. Good luck.
if [[`expr match "$line" 'SQL'` !=3]] .....
ftp ...
fi
done < $dump
You'll have to experiment with what values to put in the case statement to clean it up completely.
Also note that for any unusual characters, like '>', it is probably better to include them as a character class [>] (again requiring some experimentation on your part)
And finally, note that if you want to include white-space chars in the case match targets, either surround the phrase in dbl-quotes (") or escape each WS char like '\'.

Resources