Apache Commons CLI: prevent repeated options & force exactly one argument? - apache-commons-cli

I am using commons-cli 1.5.0. Let's say my command line syntax is the following:
Process -src <input> -dst <output>
My program ("Process") should accept exactly one -src and exactly one -dst.
However, DefaultParser allows such command lines as:
Process -src aaa.txt -src bbb.txt -src ccc.txt -dst result.txt
For the above line, getOptionValue("src") returns "aaa.txt", but getOptionValues("src") returns all 3 filenames.
Now, is there an option to disallow such syntax? To define that there can only be one -src, and if there are more, DefaultParser.parse() should throw an exception?
I've tried .hasArgs().numberOfArgs(1) and .hasArg().numberOfArgs(1),
but it did not seem to change anything...

You probably need to do this check yourself and fail the app yourself after parsing, e.g.
CommandLine cmd = parser.parse(options, args);
if (cmd.getOptionValues("src").length > 1) {
throw new IllegalStateException("Cannot handle more than one 'src' argument");
}

Related

SCP command not working in karate project - it throws command error:cannot run program scp.exe: CreateProcess error=2 [duplicate]

I'm trying to execute bash script using karate. I'm able to execute the script from karate-config.js and also from .feature file. I'm also able to pass the arguments to the script.
The problem is, that if the script fails (exits with something else than 0) the test execution continues and finishes as succesfull.
I found out that when the script echo-es something then i can access it as a result of the script so I could possibly echo the exit value and do assertion on it (in some re-usable feature), but this seems like a workaround rather than a valid clean solution. Is there some clean way of accessing the exit code without echo-ing it? Am I missing on something?
script
#!/bin/bash
#possible solution
#echo 3
exit 3;
karate-config.js
var result = karate.exec('script.sh arg1')
feture file
def result = karate.exec('script.sh arg1')
Great timing. We very recently did some work for CLI testing which I am sure you can use effectively. Here is a thread on Twitter: https://twitter.com/maxandersen/status/1276431309276151814
And we have just released version 0.9.6.RC4 and new we have a new karate.fork() option that returns an instance of Command on which you can call exitCode
Here's an example:
* def proc = karate.fork('script.sh arg1')
* proc.waitSync()
* match proc.exitCode == 0
You can get more ideas here: https://github.com/intuit/karate/issues/1191#issuecomment-650087023
Note that the argument to karate.fork() can take multiple forms. If you are using karate.exec() (which will block until the process completes) the same arguments work.
string - full command line as seen above
string array - e.g. ['script.sh', 'arg1']
json where the keys can be
line - string (OR)
args - string array
env - optional environment properties (as JSON)
redirectErrorStream - boolean, true by default which means Sys.err appears in Sys.out
workingDir - working directory
useShell - default false, auto-prepend cmd /c or sh -c depending on OS
And since karate.fork() is async, you need to call waitSync() if needed as in the example above.
Do provide feedback and we can tweak further if needed.
EDIT: here's a very advanced example that shows how to listen to the process output / log, collect the log, and conditionally exit: fork-listener.feature
Another answer which can be a useful reference: Conditional match based on OS
And here's how to use cURL for advanced HTTP tests ! https://stackoverflow.com/a/73230200/143475
In case you need to do a lot of local file manipulation, you can use the karate.toJavaFile() utility so you can convert a relative path or a "prefixed" path to an absolute path.
* def file = karate.toJavaFile('classpath:some/file.txt')
* def path = file.getPath()

Disable critic for an entire file - Parse::RecDescent precompiled parser & PerlCritic/Tidyall

I'm trying to remove an error from my sanity-checking [when I push code to my git repo, there's a hook that checks the code with perltidy & critic... using tidyall as the handler.]
The specific issue I have is with a pre-compiled Grammar Parser.... and this is not something I want to dive in & fix (sorry - that's outside my comfort zone)
If I add a simple ## no critic to the start of the file, then
perlcritic path/to/class/file.pm
comes back
path/to/class/file.pm source OK
however
tidyall --check-only -r .
comes back with
perlcritic /tmp/Code-TidyAll-Frb0/path/to/class/file.pm failed
exited with 2 - output was:
Unrestricted '## no critic' annotation at line 6, column 1. (Miscellanea::ProhibitUnrestrictedNoCritic, severity 3)
I know I can fix this in the tidyall.ini file:
[PerlCritic lib]
select = **/*.{pm}
ignore = **/class/file.pm
.... however I feel there should be a cleaner solution.
(or, why doesn't tidyall critique the same as critic?)
why doesn't tidyall critique the same as critic?
A simple perlcritic on the command line defaults to severity 5, unless you've configured something different in your ~/.perlcriticrc. The rule ProhibitUnrestrictedNoCritic defaults to severity 3, so your tidyall is running Perl::Critic with at least severity 3. As per its documentation, you can change that via something like this in the tidyall.ini:
[PerlCritic]
argv = -severity 4
And then tidyall's checks should be the same as a perlcritic -4 from the command line. (Unless you've configured custom severity levels in your .perlcriticrc.)
Update: As per your comment, you want to check everything at the "brutal" level. In that case, you can create a perlcriticrc file containing the line [-Miscellanea::ProhibitUnrestrictedNoCritic] which will disable that policy, and then point perlcritic at that file by adding the command-line argument --profile /path/to/custom/perlcriticrc.

How to resolve a "Metadata error: chr must be valid" error on Linux?

I am relatively new to the world of coding, so I am having trouble resolving an issue when running TranslocWrapper.pl tutorial_metadata.txt preprocess/ results/ --threads 2. I am trying to run the HTGTS Pipeline according to this GitHub project. This is the full error:
. Library Genome Chr Start End Strand
1 RAG1A_SRep2 hg19 chr11 36594878 36595030 -
Metadata error: chr must be valid at /home/micah/transloc_pipeline/bin/TranslocWrapper.pl line 285.
main::check_validity_of_metadata('HASH(0x2903ac8)') called at /home/micah/transloc_pipeline/bin/TranslocWrapper.pl line 248
main::read_in_meta_file() called at /home/micah/transloc_pipeline/bin/TranslocWrapper.pl line 90
I have already double-checked the successful installation of the Software Dependencies, so everything should be all good, but I am having trouble interpreting the "Metadata error: chr must be valid at ..." line. If it helps, these are the specific lines that are being called in the error:
TranslocWrapper.pl line 285:
croak "Metadata error: chr must be valid" unless grep { $_ eq $expt->{chr} } #chrlist;
TranslocWrapper.pl line 248:
check_validity_of_metadata($expt);
TranslocWrapper.pl line 90:
read_in_meta_file;
Thanks in advance for the help!
So the error is saying that one of the sequence characters in the metadata file is not present in the sequence's assembly file.
Given that this is the provided example you should assume that the data is correct and your invocation is faulty.
Have you done the TranslocPreprocess.pl preprocessing steps?
If you have try looking at the first line of the metadata file, identify the assembly entry. Ensure that the assembly file exists and that it contains the required sequence.
One common problem with this kind of code is the case of the filenames. The examples are designed to be run in Linux where filename case matters. Windows likes to pretend that case doesn't matter, this can cause problems. If you are running this code from Microsoft Windows or extracted any of the archives from within Windows this is a likely cause of the error.

jboss-cli : How do I read one specific system property using jboss-cli?

I'm new to jboss-cli and working through the 'jboss-cli recipes'.
Question
How do I read one specific property using jboss-cli? E.g.
jboss.home.dir (e.g. "-Djboss.home.dir=/path/to/my/jboss")
Xmx ("-Xmx=4G")
Context
The "CLI Recipes" documentation has this helpful example to get all system properties. However its 'too much infomration'. I want to script reading one specific property.
https://docs.jboss.org/author/display/WFLY10/CLI+Recipes#CLIRecipes-
Overview of all system properties in JBoss AS7+ including OS system
properties and properties specified on command line using -D, -P or
--properties arguments.
Standalone
[standalone#IP_ADDRESS:9999 /] /core-service=platform-mbean/type=runtime:read-attribute(name=system-properties)
Thanks in advance
You could do a :
:resolve-expression(expression=${jboss.home.dir})
You can use the cli like this:
$JBOSS_HOME/bin/jboss-cli.sh -c --command=/system-property=MY_PROPERTY:read-resource
you get an output like this:
$JBOSS_HOME/bin/jboss-cli.sh -c --command=/system-property=MY_PROPERTY:read-resource
{
"outcome" => "success",
"result" => {"value" => "4.0"}
}
which you can extract by piping into something like this:
<cli command> | grep "{\"value\"" | sed "s/.*value\" => \"\([^\"]*\)\".*/\1/"
its a bit ugly, and there are some nasty edge cases if the values were to be something like "value" => "value =" or something hideous.
In general this works OK.
Change the sed command to be a bit more specific to fix that.
This link pointed me to the answer: I can use a groovy script to get the values. From what I see the "jboss-cli command line" does not offer this flexibility.
https://developer.jboss.org/wiki/AdvancedCLIScriptingWithGroovyRhinoJythonEtc
Solution
Here's a solution for jboss home.
[For memory you can get results from "/core-service=platform-mbean/type=memory/:read-attribute(name=heap-memory-usage)"
bash
#!/bin/sh
# Note: must set jbbin to 'jboss home /bin'
groovy -cp $jbbin/client/jboss-cli-client.jar readJbossHome.groovy
Groovy
Note: this is 'quick and dirty'.
import org.jboss.as.cli.scriptsupport.*
cli = CLI.newInstance()
cli.connect()
// Define properties
myParentProp="system-properties"
myProp="jboss.home.dir"
// Retrieve and pluck values
result = cli.cmd("/core-service=platform-mbean/type=runtime:read-resource(recursive=true,include-runtime=false)")
myResult = result.getResponse().get("result")
myParentVal = myResult.get(myParentProp)
myVal = myParentVal.get(myProp)
// Print out results
println "Property detail ${myProp} is ${myVal}"
cli.disconnect()
You can also do it via Wildfly management rest call.
http://localhost:9990/management
POST
Headers = Content-Type:application/json
Body =
{
"operation":"resolve-expression",
"expression":"${jboss.home.dir}"
}
With newer Teiid DOCs I have found some useful information I thought this might be helpful to share to people coming across a similar usecase
https://access.redhat.com/documentation/en-us/jboss_enterprise_application_platform/6.3/html/administration_and_configuration_guide/configure_system_properties_using_the_management_cli
Helps Adding, Removing & Reading System Properties with jboss-cli
jboss-cli
If you have a cli command like ehsavoie suggested :resolve-expression(expression=${jboss.home.dir}) and want to use the content of the "result" property within jboss-cli you can save it in a variable. You can use backticks (`) to evaluate expressions.
simple expression
[standalone#localhost:9990 /] :resolve-expression(expression=${jboss.home.dir})
{
"outcome" => "success",
"result" => "/home/user/wildfly"
}
use in valiable
[standalone#localhost:9990 /] set wildflydirectory=`:resolve-expression(expression=${jboss.home.dir})`
[standalone#localhost:9990 /] echo $wildflydirectory
/home/user/wildfly
PowerShell
If you happen to use the PowerShell you can use a one-liner to extract even deeply nested results with the help of the cli's --output-json option and PowerShell's ConvertFrom-Json cmdlet. In this way the parsing problem from James Roberts's approach with grep and sed are gone.
$value=(Invoke-Expression "./jboss-cli.ps1 -c --command=':resolve-expression(expression=`${jboss.home.dir})' --output-json" | ConvertFrom-Json).result
It is a bit tricky to quote the command and escape the correct PowerShell special characters.

Copy all files with given extension to output directory using CMake

I've seen that I can use this command in order to copy a directory using cmake:
file(COPY "myDir" DESTINATION "myDestination")
(from this post)
My problem is that I don't want to copy all of myDir, but only the .h files that are in there. I've tried with
file(COPY "myDir/*.h" DESTINATION "myDestination")
but I obtain the following error:
CMake Error at CMakeLists.txt:23 (file):
file COPY cannot find
"/full/path/to/myDIR/*.h".
How can I filter the files that I want to copy to a destination folder?
I've found the solution by myself:
file(GLOB MY_PUBLIC_HEADERS
"myDir/*.h"
)
file(COPY ${MY_PUBLIC_HEADERS} DESTINATION myDestination)
this also works for me:
install(DIRECTORY "myDir/"
DESTINATION "myDestination"
FILES_MATCHING PATTERN "*.h" )
The alternative approach provided by jepessen does not take into account the fact that sometimes the number of files to be copied is too high. I encountered the issue when doing such thing (more than 110 files)
Due to a limitation on Windows on the number of characters (2047 or 8191) in a single command line, this approach may randomly fail depending on the number of headers that are in the folder. More info here https://support.microsoft.com/en-gb/help/830473/command-prompt-cmd-exe-command-line-string-limitation
Here is my solution:
file(GLOB MY_HEADERS myDir/*.h)
foreach(CurrentHeaderFile IN LISTS MY_HEADERS)
add_custom_command(
TARGET MyTarget PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CurrentHeaderFile} ${myDestination}
COMMENT "Copying header: ${CurrentHeaderFile}")
endforeach()
This works like a charm on MacOS. However, if you have another target that depends on MyTarget and needs to use these headers, you may have some compile errors due to not found includes on Windows. Therefore you may want to prefer the following option that defines an intermediate target.
function (CopyFile ORIGINAL_TARGET FILE_PATH COPY_OUTPUT_DIRECTORY)
# Copy to the disk at build time so that when the header file changes, it is detected by the build system.
set(input ${FILE_PATH})
get_filename_component(file_name ${FILE_PATH} NAME)
set(output ${COPY_OUTPUT_DIRECTORY}/${file_name})
set(copyTarget ${ORIGINAL_TARGET}-${file_name})
add_custom_target(${copyTarget} DEPENDS ${output})
add_dependencies(${ORIGINAL_TARGET} ${copyTarget})
add_custom_command(
DEPENDS ${input}
OUTPUT ${output}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${input} ${output}
COMMENT "Copying file to ${output}."
)
endfunction ()
foreach(HeaderFile IN LISTS MY_HEADERS)
CopyFile(MyTarget ${HeaderFile} ${myDestination})
endforeach()
The downside indeed is that you end up with multiple target (one per copied file) but they should all end up together (alphabetically) since they start with the same prefix ORIGINAL_TARGET -> "MyTarget"