In Bash, how can I check the version of Artistic Style? - bash

I am writing a portable procedure (i.e. I am not using dpkg) in a Bash script that ensures that certain program versions are available. I can get the version of indent in the following way:
version_indent="$(echo "$(indent --version)" | cut -d" " -f3)"
I don't understand why I can't get the version of astyle in a similar way:
version_astyle="$(echo "$(astyle --version)" | cut -d" " -f4)"
When I do the former, I get the actual version; when I do the latter, I get the full string returned by astyle printed to the terminal and nothing saved to the variable. What's going wrong?

Related

Shell script - remove all before and after

Find the next link if the Link header contains rel=next..
Getting the link header can result in different strings.. I need to find the next link.
e.g.
Link: <http://mygithub.com/api/v3/organizations/20/repos?page=1>; rel=prev, <http://mygithub.com/api/v3/organizations/20/repos?page=3>; rel=next, <http://mygithub.com/api/v3/organizations/20/repos?page=4>; rel=last, <http://mygithub.com/api/v3/organizations/20/repos?page=1>;
would be http://mygithub.com/api/v3/organizations/20/repos?page=3
Link: <http://mygithub.com/api/v3/organizations/4/repos?page=2>; rel="next", <http://mygithub.com/api/v3/organizations/4/repos?page=2>; rel="last"
would be http://mygithub.com/api/v3/organizations/4/repos?page=2
Played with sed and parameter expansion - not that experienced so got stuck :)
Please be aware that parsing HTML with non-html tools it fraught with peril; you will see that this works, and assume you can get away with it always. You'll spend hours trying to get the next level of complexity to work, when you should be studying how to use html-aware tools. Don't say we didn't warn you (-;, but
printf "<http://mygithub.com/api/v3/organizations/20/repos?page=1>; rel=prev, <http://mygithub.com/api/v3/organizations/20/repos?page=3>; rel=next, <http://mygithub.com/api/v3/organizations/20/repos?page=4>; rel=last, <http://mygithub.com/api/v3/organizations/20/repos?page=1>;\n" \
| awk -F" " '{
for(i=1;i<=NF;i++){
if ($i == "rel=next,") {
gsub(/[<>]/,"",$(i-1);sub(/;$/,"",$(i-1))
print $(i-1)
}
}
}'
produces required output:
http://mygithub.com/api/v3/organizations/20/repos?page=3
To save the output of a script section into a variable, you wrap the code for command-substitution, in this case
nextReposLink=$( printf .... | awk '....' )
#-------------^^--------------------------^
The ^ pointed items are modern syntax for command-substitution. The code inside of $( ... ) is executed and the standard output is passed as a argument to the invoking command line. (The original syntax for command substitution is/was `cmds` and works the same in the simple case var=`cmds` . You can nest modern cmd-substitution easily, whereas the old version requires a lot of escape character fiddling. Avoid it if you can.
Note that about any s/str/rep/ that sed can do, awk can do the same, but requires the use of the sub(/regx/, "repl", "str") or gsub(sameArgs) functions. In this particular case, you may need to escape the <> like \<\>.
Be sure to always dbl-quote the use of variables, i.e. echo "$nextReposLink".
IHTH
Well - I put one of your URL strings in a text file and was able to pull out the first URL with two cuts.
[root#oelinux2 ~]# cat test
Link: <http://mygithub.com/api/v3/organizations/20/repos?page=1>; rel=prev, <http://mygithub.com/api/v3/organizations/20/repos?page=3>; rel=next, <http://mygithub.com/api/v3/organizations/20/repos?page=4>; rel=last, <http://mygithub.com/api/v3/organizations/20/repos?page=1>;
Then with using cut:
cat test | cut -d "<" -f2 | cut -d ">" -f1
[root#oelinux2 ~]# cat test | cut -d "<" -f2 | cut -d ">" -f1
http://mygithub.com/api/v3/organizations/20/repos?page=1
That's one option - if you are just looking to get the first URL in the string. Basically - that's just grabbing what's between the two delimiters "<" and ">"
With Cut:
-d is the 'delimiter'
-f is the field you want to get.
If you wanted to get a later URL in that string, you could change the fields (-f #) and see what you get :)

format TAG release ref for Github Workflow Action

I would like to tag my release with v as prefix and product type as a suffix.
E.g. Initial release v1.0.0-alpha01-internal or v1.0.0-alpha01-external
Now I am running a GitHub action workflow to publish a release.
# The GITHUB_REF tag comes in the format 'refs/tags/xxx'.
# So if we split on '/' and take the 3rd value, we can get the release name.
run: |
NEW_VERSION=$(echo "${GITHUB_REF}" | cut -d "/" -f3)
echo "New version: ${NEW_VERSION}"
with the above code snippet, I get my new version v1.0.0-alpha01-internal or v1.0.0-alpha01-external now I don't want my version to be the same as TAG so I would like to cut v from start and -internal or -external from the end of the release TAG.
The expectation of a new version would be 1.0.0-alpha01
NEW_VERSION=$(echo "${GITHUB_REF}" | cut -d "/" -f3) | cut -c 2- | cut -c 1-13 maybe?
at this point your issue is just with string manipulation in Bash and has nothing to do with GH or GH Actions.
So you can just find what is the best way to modify a string in Bash. I recommend you start here:
https://www.baeldung.com/linux/bash-string-manipulation
TAG_VERSION=v1.0.0-alpha01-internal
TAG_VERSION=$(echo ${TAG_VERSION//v/})
TAG_VERSION=$(echo ${TAG_VERSION//-internal/})
TAG_VERSION=$(echo ${TAG_VERSION//-external/})
Or using sed
There is probably a better regex to use here but you can decide based on all the use cases
echo $TAG_VERSION | sed 's/-internal//g' | sed 's/-external//g' | sed 's/v//g'

multi process bash within fzf --preview feature

I am trying to use fzf in the following manner, I would like to be able to search for a term within my codebase and then with the preview window be able to see the file which contains the string I am searching for at the line where the string is found.
So far I have managed to fuzzy search through the codebase for various terms by piping a ripgrep search of all files in the directory and below. And I have used cut to parse out the file name for cat or tail to read and print to the preview window. This is the command used for that.
rg . -n | fzf --preview-"cut -d":" -f1 <<< {} | xargs cat"
Note the string represented by {} is in the following format:
myfile.c:72:The string I am fuzzy searching
My issue is that I cannot parse out both the filename and the line number.
I have tried passing a bashscript within the preview command as well as using $() in the following example. (Note that here I use tail with the --lines+N argument to print the file after line N)
rg . -n | fzf --preview-"tail $(cut -d":" -f1 <<< {}) --lines=+$(cut -d":" -f2 <<< {})"
This does not work nor does a variety of variants on this attempt. Any help or feedback is appreciated.
Edit(1) :
I've tried to split it into an array like so
rg . -n | fzf --preview="IFS=":" read -r -a arr <<< {}| xargs tail ${arr[0]} --lines=+${arr[1]}"
This works in that the preview does show the file at the line where the string is found however it does not update as I cycle through other fuzzy found suggestions.
So I eventually figured out a solution that works.
It involves calling a separate script from my subprocess running inside --preview. I used the following script to take the string which fzf passes to --preview (in the format of filename:linenumber:found_string) and then used bat to render a preview window with syntax highlighting.
This method is pretty good but is somewhat resource intensive. I'm hoping to lessen the load by adding to the ignore glob and using ripgrep rather then find as it seems that is more efficient.
The bashscript I call string2arg.sh
#!/bin/bash
string2arg() {
export arg_filename=$(cut -d":" -f1 <<< $1);
export arg_linenum=$(cut -d":" -f2 <<< $1);
min_offset=25
let max_offset="min_offset*3"
min=0
if (($min_offset < $arg_linenum)); then
let min="arg_linenum-$min_offset"
fi
let max="arg_linenum+$max_offset"
bat --color=always --highlight-line $arg_linenum --style=header,grid,numbers --line-range $min:$max $arg_filename;
}
This is then called from my fzf alias for searching as such:
alias fsearch='rg . -n -g "!*.html" | fzf --preview="source $SC/string2arg.sh; string2arg {}"'
where $SC is the path to my bashscript string2arg.sh.
If I'm searching for a term with the intent to open the file its found in at the line its found in I use the following bash alias.
alias vfsearch='export vfile=$(fsearch);vim +$(cut -d":" -f2 <<< $vfile) $(cut -d":" -f1 <<< $vfile)'
Also I happen to use the following defaults for
fzf and find them to work for me although since I've moved to tmux I find it sometimes better to show the preview window above rather then to the side.
export FZF_DEFAULT_COMMAND="fd --type file --color=always"
export FZF_DEFAULT_OPTS="--reverse --inline-info --ansi"
export FZF_COMPLETION_TRIGGER=']]'
I find this extremely useful and am planning on moving it inside of my vim sessions. Hope it helps others !
Screenshot to better illustrate the use case.

grep pipe searching for one word, not line

For some reason I cannot get this to output just the version of this line. I suspect it has something to do with how grep interprets the dash.
This command:
admin#DEV:~/TEMP$ sendemail
Yields the following:
sendemail-1.56 by Brandon Zehm
More output below omitted
The first line is of interest. I'm trying to store the version to variable.
TESTVAR=$(sendemail | grep '\s1.56\s')
Does anyone see what I am doing wrong? Thanks
TESTVAR is just empty. Even without TESTVAR, the output is empty.
I just tried the following too, thinking this might work.
sendemail | grep '\<1.56\>'
I just tried it again, while editing and I think I have another issue. Perhaps im not handling the output correctly. Its outputting the entire line, but I can see that grep is finding 1.56 because it highlights it in the line.
$ TESTVAR=$(echo 'sendemail-1.56 by Brandon Zehm' | grep -Eo '1.56')
$ echo $TESTVAR
1.56
The point is grep -Eo '1.56'
from grep man page:
-E, --extended-regexp
Interpret PATTERN as an extended regular expression (ERE, see below). (-E is specified by POSIX.)
-o, --only-matching
Print only the matched (non-empty) parts of a matching line, with each such part on a separate output
line.
Your regular expression doesn't match the form of the version. You have specified that the version is surrounded by spaces, yet in front of it you have a dash.
Replace the first \s with the capitalized form \S, or explicit set of characters and it should work.
I'm wondering: In your example you seem to know the version (since you grep for it), so you could just assign the version string to the variable. I assume that you want to obtain any (unknown) version string there. The regular expression for this in sed could be (using POSIX character classes):
sendemail |sed -n -r '1 s/sendemail-([[:digit:]]+\.[[:digit:]]+).*/\1/ p'
The -n suppresses the normal default output of every line; -r enables extended regular expressions; the leading 1 tells sed to only work on line 1 (I assume the version appears in the first line). I anchored the version number to the telltale string sendemail- so that potential other numbers elsewhere in that line are not matched. If the program name changes or the hyphen goes away in future versions, this wouldn't match any longer though.
Both the grep solution above and this one have the disadvantage to read the whole output which (as emails go these days) may be long. In addition, grep would find all other lines in the program's output which contain the pattern (if it's indeed emails, somebody might discuss this problem in them, with examples!). If it's indeed the first line, piping through head -1 first would be efficient and prudent.
jayadevan#jayadevan-Vostro-2520:~$ echo $sendmail
sendemail-1.56 by Brandon Zehm
jayadevan#jayadevan-Vostro-2520:~$ echo $sendmail | cut -f2 -d "-" | cut -f1 -d" "
1.56

Calling sed in a qmake script

Here is my problem:
I want have my qmake script detect my opencv version and save the result in the CONFIG variable. I need the result to have this form : "opencv20","opencv21","opencv22",etc.
I know that I can use the system() function to call bash commands and wanted to use something like this :
CONFIG += opencv$$system(pkg-config --modversion opencv | cut -d. -f'1,2' | sed 's/\.//g')
It works fine in my terminal, but qmake gives me "opencv2." when I try to print the output. The outputs of pkg-config and cut commands alone are correct so I assume the sed call is confusing qmake somehow... any hints ?
system() commands are executed in a subshell. That's why you have to escape your strings:
CONFIG += opencv$$system(pkg-config --modversion opencv | cut -d . -f \'1,2\' | sed \'s/\.//g\')

Resources