I tried to improve on davfive's answer and make it more robust.
It now handles spaces in filenames as well as input such as:
[Machine1]
app = version1 # comment
#unusedApp = versionX
The rest of a line after a #
is ignored, and calling it with just a filename lists sections by default.
Also made it a script rather than a function:
#!/usr/bin/env bash
# get property value in ini file
# original author: davfive
# https://stackoverflow.com/a/54597545
# edited by: cherrynoize
if [ "$1" == "-h" ] || [ "$1" == "--help" ]; then
cat<<EOF
get property value in ini file
usage: iniget <file> [-l|--list|<section> [property]]
EOF
exit
fi
inifile="$1"
if [ ! -f "$1" ]; then
echo "error: no file '$1'"
exit 4
fi
if [ -z "$2" ] || [ "$2" = "-l" ] || [ "$2" == "--list" ]; then
for section in $(cat "$inifile" | grep "\[" | sed -e "s#\[##g" | sed -e "s#\]##g"); do
echo "$section"
done
exit
fi
section="$2"
[ $# -eq 3 ] && property="$3"
remove_section_header () {
sed "s/^\[$section\]//" <<< "$1"
}
strip_comments () {
sed -r -e 's/^#.*//' -e 's/[[:space:]]+#.*//' <<< "$1"
}
strip_spaces () {
tr -d " " <<< "$1"
}
# https://mcmap.net/q/1393573/-parsing-ini-file-in-bash
# turn ini sections => [section-name]property=value
mapfile lines <<< "$(awk '/\[/{prefix=$0; next} $1{print prefix $0}' "$inifile")"
for line in "${lines[@]}"; do
# verify property belongs to requested section
if [[ "$line" != \[$section\]* ]]; then
continue
fi
# strip property line
line="$(remove_section_header "$line")"
line="$(strip_comments "$line")"
line="$(strip_spaces "$line")"
if [ "$line" = "" ]; then
continue
fi
if [ -z "$property" ]; then
echo "$line"
continue
fi
if [[ $line = $property=* ]]; then
sed -e "s/^$property=//" <<< "$line"
exit
fi
done
Also, this is a simpler oneliner for doing the same thing:
grep -oP "(?<=^$key = ).*" "$filename" | cut -d' ' -f2-
Though it only supports a single form:
key = value
But actually with just sed
:
sed -n "s/^$key[[:space:]]=[[:space:]]//p" "$filename"
You can handle any amount of whitespace/tab separators.
By the way, here's a script to edit .ini file values as well:
#!/usr/bin/env bash
# set property value in ini file
# author: cherrynoize
# https://github.com/cherrynoize/dotfiles
# initialize current value to empty
cur_value=
if [ "$1" == "-h" ] || [ "$1" == "--help" ]; then
cat<<EOF
set property value in ini file
usage: iniset <file> <section> <property>
EOF
exit
fi
if [ "$#" -lt 4 ]; then
echo "error: not enough arguments"
exit 1
fi
inifile="$1"
section="$2"
property="$3"
new_value="$4"
if [ ! -f "$1" ]; then
echo "error: no file '$1'"
exit 4
fi
remove_section_header () {
sed "s/^\[$section\]//" <<< "$1"
}
strip_comments () {
sed -r -e 's/^#.*//' -e 's/[[:space:]]+#.*//' <<< "$1"
}
strip_spaces () {
tr -d " " <<< "$1"
}
# https://mcmap.net/q/1393573/-parsing-ini-file-in-bash
# turn ini sections => [section-name]property=value
# maintain header and empty lines track line numbers correctly
mapfile lines <<< "$(awk '/\[/{section=$0; print; next} !$1{print ""; next} {print section $0}' "$inifile")"
# keep track of line number to update the correct line
line_number=0
for line in "${lines[@]}"; do
(( line_number++ ))
# verify property belongs to requested section
if [[ "$line" != \[$section\]* ]]; then
continue
fi
# strip property line
line="$(remove_section_header "$line")"
line="$(strip_comments "$line")"
line="$(strip_spaces "$line")"
if [ "$line" = "" ]; then
continue
fi
if [[ $line = $property=* ]]; then
cur_value="$(sed -e "s/^$property=//" <<< "$line")"
sed -i "${line_number}s/$cur_value"'$'"/$new_value/" "$inifile"
exit
fi
done
if [ -z "$cur_value" ]; then
echo "error: property '$property' not found in '$inifile'"
exit 255
fi
You can use it like so:
iniset <file> <section> <property>