Describe: "Require at least one option" in usage - bash

What is the proper way to convey the notion of "one or more of the following option flags must be present -a -b -c" in a bash usage message?
(Details on similar problems/solutions, to show my current attempts follow.)
I know how to specify a required argument:
usage: ./my_script.sh arg_matey.html
I also know how to specify an optional argument such as -x:
usage: ./my_script.sh [-x] marks_the_spot.py
I even know how to make mutually exclusive optional arguments:
usage: ./my_script.sh [-p | -i | -r | -a | -t | -e] walk_the_plank.txt
But no combination of those seems to satisfy the notion of 1+ option from a list must be used.

Consider using a multi line usage statement.
usage: ./my_script.sh flag walk_the_plank.txt
Flags: [-p | -i | -r | -a | -t | -e]

Related

xargs with variable number of arguments

I am trying to create a command to show logs for all running allocations in a Nomad cluster. I can get the allocation IDs with this command:
curl -s $NOMAD_ADDR/v1/allocations | jq -r '.[] | select(.JobID=="MY_JOB_NAME") | "\(.ID)"'
From there I would like to run multitail with nomad logs <allocation> -tail -f for each allocation so that I can watch all of the logs at once. The syntax of a multitail call looks like this:
multitail [options] -l "shell 1 command" -l "shell 2 command" -l...
If you open 5 shells, then you need 5 -l arguments.
I do not see this functionality in the xargs manual, but I need something like xargs multitail --arg-for-each "-l my shell command {}". Is it possible to use xargs to construct commands with variable numbers of arguments in this way? If not, is there any alternative that I can use to do so?
Your input is a list of ids. For the purposes of this answer, let's say that looks like:
foo
bar
baz
You want to transform this into:
multitail \
-l "nomad logs foo -tail -f" \
-l "nomad logs bar -tail -f" \
-l "nomad logs baz -tail -f"
Perhaps something like this would work:
eval multitail $(command_that_generates_ids | xargs -IID echo "-l 'nomad logs ID -tail -f'")

grep: Unknown option -C

I want grep to print 2 lines before and after the matching line.
I am using GnuWin32 grep(2.5.4). But it is showing error as follows:
grep: Unknown option -C
Usage: grep [-clqinsvxEF] [-bI] [-e pattern] [-f patternfile] [pattern] [file ...]
As per my understanding from the error, the -C option is not supported by GnuWin32.
So my doubt is The GnuWin32 grep will not support -C option or any updated version is there for GnuWin32 grep which would support -C option....

Sort in Makefile

I have this in my makefile
test:
cat t.txt | sort -t $$'\t' -k 2,2
But "make test" gives me this error
cat t.txt | sort -t $'\t' -k 2,2
sort: multi-character tab `$\t'
make: * [test] Error 2
Works fine on Redhat linux but fails on Ubuntu linux
The $'\t' syntax you're trying to use is a bash-ism, but by default gmake uses /bin/sh as the shell. You can either override the SHELL variable in your makefile, as in:
SHELL=/bin/bash
or explicitly invoke bash for this specific command:
test:
bash -c "cat t.txt | sort -t $$'\t' -k 2,2"
Q: What OS is this failing on?
SUGGESTION:
How do I sort a tab separated file on the nth column using cygwin sort?
http://linux.derkeiler.com/Mailing-Lists/Fedora/2008-03/msg02180.html
You can substitute an actual tab, e.g. sort -t "<Ctl-V><Tab>"

Command composition in bash

So I have the equivalent of a list of files being output by another command, and it looks something like this:
http://somewhere.com/foo1.xml.gz
http://somewhere.com/foo2.xml.gz
...
I need to run the XML in each file through xmlstarlet, so I'm doing ... | xargs gzip -d | xmlstarlet ..., except I want xmlstarlet to be called once for each line going into gzip, not on all of the xml documents appended to each other. Is it possible to compose 'gzip -d' 'xmlstarlet ...', so that xargs will supply one argument to each of their composite functions?
Why not read your file and process each line separately in the shell? i.e.
fileList=/path/to/my/xmlFileList.txt
cat ${fileList} \
| while read fName ; do
gzip -d ${fName} | xmlstartlet > ${fName}.new
done
I hope this helps.
Although the right answer is the one suggested by shelter (+1), here is a one-liner "divertimento" providing that the input is the proposed by Andrey (a command that generates the list of urls) :-)
~$ eval $(command | awk '{a=a "wget -O - "$0" | gzip -d | xmlstartlet > $(basename "$0" .gz ).new; " } END {print a}')
It just generates a multi command line that does wget http://foo.xml.gz | gzip -d | xmlstartlet > $(basenname foo.xml.gz .gz).new for each of the urls in the input; after the resulting command is evaluated
Use GNU Parallel:
cat filelist | parallel 'zcat {} | xmlstarlet >{.}.out'
or if you want to include the fetching of urls:
cat urls | parallel 'wget -O - {} | zcat | xmlstarlet >{.}.out'
It is easy to read and you get the added benefit of having on job per CPU run in parallel. Watch the intro video to learn more: http://www.youtube.com/watch?v=OpaiGYxkSuQ
If xmlstarlet can operate on stdin instead of having to pass it a filename, then:
some command | xargs -i -n1 sh -c 'zcat "{}" | xmlstarlet options ...'
The xargs option -i means you can use the "{}" placeholder to indicate where the filename should go. Use -n 1 to indicate xargs should only one line at a time from its input.

Strange behavior of uniq on darwin shells

I've used 'uniq -d -c file' in many shell scripts on linux machines, and it works.
On my MAC (OS X 10.6.7 with developer tools installed) it doesn't seems to work:
$ uniq -d -c testfile.txt
usage: uniq [-c | -d | -u] [-i] [-f fields] [-s chars] [input [output]]
It would be nice if anyone could checks this.
Well, it's right there in the Usage message. [ -c | -d | -u] means you can use one of those possibilities, not two.
Since OSX is based on BSD, you can check that here or, thanks to Ignacio, the more Apple-specific one here.
If you want to achieve a similar output, you could use:
do_your_thing | uniq -c | grep -v '^ *1 '
which will strip out all those coalesced lines that have a count of one.
You can try this awk solution
awk '{a[$0]++}END{for(i in a)if(a[i]>1){ print i ,a[i] } }' file

Resources