how to use * in sh string comparison - sh

How to correctly use * in sh? I tried googling it but couldn't find anything. The following echo ture. why is that?
file="test test"
if [ "$file" != "te"* ]
then
echo true
else
echo false
fi

To avoid all the potential problems, when using POSIX shell, you should consider using the old expr regex or match expressions. Your choices are:
#!/bin/sh
file="test test"
if [ $(expr "$file" : "te.*") -gt 0 ]
then
echo true
else
echo false
fi
or
if [ $(expr substr "$file" 1 2) = "te" ]
then
echo true
else
echo false
fi
Not elegant, but they are the proper tools for the shell. A short explanation of each and the expr syntax for each is:
string : regularExp : returns the length of string if both sides match,
returns 0 otherwise
match string regularExp : same as the previous one
substr string start length : returns the substring of string starting from
start and consisting of length characters

I did a bit of googling and found a good bash scripting resource:
Advanced Bash-Scripting Guide
There is a segment that answers your question:
[[ $a == z* ]] # True if $a starts with an "z" (pattern matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).
[ $a == z* ] # File globbing and word splitting take place.
[ "$a" == "z*" ] # True if $a is equal to z* (literal matching).
So in your case the condition should be:
if [[ file != te* ]]

Related

Bash String Comparison With Regex Not Working

I'm trying to do a simple string comparison in zsh where one the arguments is the result of a sed call.
Yet I keep getting "zsh: missing end of string"
[ (sed -En s/'(^.+)-SNAPSHOT'/'\1'/p ./app_version.txt) = "1.20.11" ]
The app_version.txt file contains a Maven version. sed is stripping out "SNAPSHOT".
app_version.txt
1.20.11-SNAPSHOT
It's because the output of your sed needs to be double quoted. Try this:
[ "$(sed -En s/'(^.+)-SNAPSHOT'/'\1'/p ./app_version.txt)" = "1.20.11" ]
In your case, you don't even need regular expressions. You could do it with
if [[ $(<app_version.txt) == 1.20.11-SNAPSHOT ]]
or, if you want to make the SNAPSHOT part optional, something like
if [[ $(<app_version.txt) == 1.20.11-* ]]

posix sh: how to count number of occurrences in a string without using external tools?

In bash, it can be done like this:
#!/bin/bash
query='bengal'
string_to_search='bengal,toyger,bengal,persian,bengal'
delimiter='|'
replace_queries="${string_to_search//"$query"/"$delimiter"}"
delimiter_count="${replace_queries//[^"$delimiter"]}"
delimiter_count="${#delimiter_count}"
echo "Found $delimiter_count occurences of \"$query\""
Output:
Found 3 occurences of "bengal"
The caveat of course is that the delimiter cannot occur in 'query' or 'string_to_search'.
In POSIX sh, string replacement is not supported. Is there a way this can be done in POSIX sh using only shell builtins?
#!/bin/sh
query='bengal'
string_to_search='bengal,toyger,bengal,persian,bengal'
ct() (
n=0
IFS=,
q=$1
set $2
for t in "$#"; do
if [ "$t" = "$q" ]; then
n=$((n + 1))
fi
done
echo $n
)
n=$(ct "$query" "$string_to_search")
printf "found %d %s\n" $n $query
Though I'm not sure what the point is. If you've got a posix shell,
you also almost certainly have printf, sed, grep, and wc.
printf '%s\n' "$string_to_search" | sed -e 's/,/\n/g' | grep -Fx "$query" | wc -l
Think I got it...
#!/bin/sh
query='bengal'
string_to_search='bengal,toyger,bengal,persian,bengal'
i=0
process_string="$string_to_search"
while [ -n "$process_string" ]; do
case "$process_string" in
*"$query"*)
process_string="${process_string#*"$query"}"
i="$(( i + 1 ))"
;;
*)
break
;;
esac
done
echo "Found $i occurences of \"$query\""

How to remove YAML frontmatter from markdown files?

I have markdown files that contain YAML frontmatter metadata, like this:
---
title: Something Somethingelse
author: Somebody Sometheson
---
But the YAML is of varying widths. Can I use a Posix command like sed to remove that frontmatter when it's at the beginning of a file? Something that just removes everything between --- and ---, inclusive, but also ignores the rest of the file, in case there are ---s elsewhere.
I understand your question to mean that you want to remove the first ----enclosed block if it starts at the first line. In that case,
sed '1 { /^---/ { :a N; /\n---/! ba; d} }' filename
This is:
1 { # in the first line
/^---/ { # if it starts with ---
:a # jump label for looping
N # fetch the next line, append to pattern space
/\n---/! ba; # if the result does not contain \n--- (that is, if the last
# fetched line does not begin with ---), go back to :a
d # then delete the whole thing.
}
}
# otherwise drop off the end here and do the default (print
# the line)
Depending on how you want to handle lines that begin with ---abc or so, you may have to change the patterns a little (perhaps add $ at the end to only match when the whole line is ---). I'm a bit unclear on your precise requirements there.
If you want to remove only the front matter, you could simply run:
sed '1{/^---$/!q;};1,/^---$/d' infile
If the first line doesn't match ---, sed will quit; else it will delete everything from the 1st line up to (and including) the next line matching --- (i.e. the entire front matter).
If you don't mind the "or something" being perl.
Simply print after two instances of "---" have been found:
perl -ne 'if ($i > 1) { print } else { /^---/ && $i++ }' yaml
or a bit shorter if you don't mind abusing ?: for flow control:
perl -ne '$i > 1 ? print : /^---/ && $i++' yaml
Be sure to include -i if you want to replace inline.
you use a bash file, create script.sh and make it executable using chmod +x script.sh and run it ./script.sh.
#!/bin/bash
#folder articles contains a lot of markdown files
files=./articles/*.md
for f in $files;
do
#filename
echo "${f##*/}"
#replace frontmatter title attribute to "title"
sed -i -r 's/^title: (.*)$/title: "\1"/' $f
#...
done
This AWK based solution works for files with and without FrontMatter, doing nothing in the later case.
#!/bin/sh
# Strips YAML FrontMattter from a file (usually Markdown).
# Exit immediately on each error and unset variable;
# see: https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
set -Ee
print_help() {
echo "Strips YAML FrontMattter from a file (usually Markdown)."
echo
echo "Usage:"
echo " `basename $0` -h"
echo " `basename $0` --help"
echo " `basename $0` -i <file-with-front-matter>"
echo " `basename $0` --in-place <file-with-front-matter>"
echo " `basename $0` <file-with-front-matter> <file-to-be-without-front-matter>"
}
replace=false
in_file="-"
out_file="/dev/stdout"
if [ -n "$1" ]
then
if [ "$1" = "-h" ] || [ "$1" = "--help" ]
then
print_help
exit 0
elif [ "$1" = "-i" ] || [ "$1" = "--in-place" ]
then
replace=true
in_file="$2"
out_file="$in_file"
else
in_file="$1"
if [ -n "$2" ]
then
out_file="$2"
fi
fi
fi
tmp_out_file="$out_file"
if $replace
then
tmp_out_file="${in_file}_tmp"
fi
awk -e '
BEGIN {
is_first_line=1;
in_fm=0;
}
/^---$/ {
if (is_first_line) {
in_fm=1;
}
}
{
if (! in_fm) {
print $0;
}
}
/^(---|...)$/ {
if (! is_first_line) {
in_fm=0;
}
is_first_line=0;
}
' "$in_file" >> "$tmp_out_file"
if $replace
then
mv "$tmp_out_file" "$out_file"
fi

how to write a not-so complex if-else statement in Bourne shell?

I'm trying to get a not-so complex if-else to work on my FreeBSD box but I'm getting error with the second part of the condition.
basically, this is what I'm trying to do
if not file-exists or (file-exists and string exists in file) then
do this
else
do something else
this is the actual code I'm using
if [ ! -f /boot/loader.conf ] || [[ -f /boot/loader.conf ] && ! grep -Fqx "zfs_l
oad" /boot/loader.conf ]; then
echo "found"
else
echo "not found"
fi
It gives me an error about "[[". I tried adding/removing brackets to no avail.
I've also searched the net for similar examples but the ones I've seen are very simplistic (i.e. if var=value then do this)
I could separate the conditions into 2 "ifs" but I think it can be done in 1 and I'm want to know "advance" if-else in bourne as well. :)
Any help would be greatly appreciated.
thanks :)
Use { ... } for grouping without the overhead and side-effects of a subshell (as created by ( ... )). [[ ]] is a different syntax, only available in ksh derivatives such as bash, which replaces [ ... ] with a less-error-prone alternative; it isn't available in baseline POSIX shells.
[ ! -f /boot/loader.conf ] || \
{ [ -f /boot/loader.conf ] && ! grep -Fqx "zfs_load" /boot/loader.conf; }

shell script: `[[` not working as expected

I have a script:
ENABLE_SYSLOG=true
test -r /etc/default/inotifywait && . /etc/default/inotifywait || exit 99
test -d $INOTIFY_FOLDER || exit 100
inotifywait -mrq -e ATTRIB --format '%w%f' "$INOTIFY_FOLDER" | while IFS= read -r FILE
do
if [ -f $FILE ];then
# If file
if [ `stat -c %a $FILE` != "664" ] ;then
CHMOD_LOG=$(chmod -v 664 "$FILE")
[[ "$ENABLE_SYSLOG" = true ]] && logger -t inotifywait -p user.info "$CHMOD_LOG" &
fi
else
# If directory
if [ `stat -c %a $FILE` != "2775" ] ;then
CHMOD_LOG=$(chmod -v 2775 "$FILE")
[[ "$ENABLE_SYSLOG" = true ]] && logger -t inotifywait -p user.info "$CHMOD_LOG" &
fi
fi
done
Why my condition
[[ "$ENABLE_SYSLOG" = true ]] && logger -t inotifywait -p user.info "$CHMOD_LOG"
Not work ?
If I remove
[[ "$ENABLE_SYSLOG" = true ]] &&
Then everything is fine.
I try to remove ampersand (&), try other condition (as you can see in example), try if instead of [[ - but all in vain.
Any condition is not work
[[ ... ]] is present in most advanced shells (ksh, bash, and zsh) but is missing in POSIX sh and in Ubuntu /bin/sh (which is a symlink to /bin/dash).
You can either replace [[ ... ]] with [ ... ] or test ... (both are equivalent, the second makes it clear that test is a command while [ ... ] appears as if it is special syntax), or (unless the script is explicitly invoked as sh FILENAME), start it with #!/bin/bash. Note that [[ ... ]] is parsed differently than test or [ ... ], as [[ is a reserved word, while [ and test are merely builtin commands. Specifically, field splitting and globbing are not done between [[ and ]], while = and == in this context do shell pattern matching if neither string is quoted. Furthermore, the =~ operator to do regexp matching is not present in test or [.