Can't find package from application written in Tcl - scripting

Thanks to the comments, better understand the problem a bit. The variables:
thufir#dur:~/tcl/packages$
thufir#dur:~/tcl/packages$ echo 'puts $auto_path' | tclsh
/usr/share/tcltk/tcl8.6 /usr/share/tcltk /usr/lib /usr/local/lib/tcltk /usr/local/share/tcltk /usr/lib/tcltk/x86_64-linux-gnu /usr/lib/tcltk /usr/lib/tcltk/tcl8.6
thufir#dur:~/tcl/packages$
thufir#dur:~/tcl/packages$ echo 'puts $tcl_pkgPath' | tclsh
/usr/local/lib/tcltk /usr/local/share/tcltk /usr/lib/tcltk/x86_64-linux-gnu /usr/lib/tcltk /usr/share/tcltk /usr/lib/tcltk/tcl8.6 /usr/lib
thufir#dur:~/tcl/packages$
code:
thufir#dur:~/tcl/packages$
thufir#dur:~/tcl/packages$ ll
total 16
drwxrwxr-x 2 thufir thufir 4096 May 4 02:22 ./
drwxrwxr-x 6 thufir thufir 4096 May 4 02:22 ../
-rw-rw-r-- 1 thufir thufir 215 May 4 02:21 foo.tcl
-rw-rw-r-- 1 thufir thufir 1207 May 4 02:20 tutstack.tcl
thufir#dur:~/tcl/packages$
thufir#dur:~/tcl/packages$ cat foo.tcl
package require tutstack 1.0
set stack [tutstack::create]
foreach num {1 2 3 4 5} { tutstack::push $stack $num }
while { ![tutstack::empty $stack] } {
puts "[tutstack::pop $stack]"
}
tutstack::destroy $stack
thufir#dur:~/tcl/packages$
thufir#dur:~/tcl/packages$ cat tutstack.tcl
# Register the package
package provide tutstack 1.0
package require Tcl 8.5
# Create the namespace
namespace eval ::tutstack {
# Export commands
namespace export create destroy push pop peek empty
# Set up state
variable stack
variable id 0
}
# Create a new stack
proc ::tutstack::create {} {
variable stack
variable id
set token "stack[incr id]"
set stack($token) [list]
return $token
}
# Destroy a stack
proc ::tutstack::destroy {token} {
variable stack
unset stack($token)
}
# Push an element onto a stack
proc ::tutstack::push {token elem} {
variable stack
lappend stack($token) $elem
}
# Check if stack is empty
proc ::tutstack::empty {token} {
variable stack
set num [llength $stack($token)]
return [expr {$num == 0}]
}
# See what is on top of the stack without removing it
proc ::tutstack::peek {token} {
variable stack
if {[empty $token]} {
error "stack empty"
}
return [lindex $stack($token) end]
}
# Remove an element from the top of the stack
proc ::tutstack::pop {token} {
variable stack
set ret [peek $token]
set stack($token) [lrange $stack($token) 0 end-1]
return $ret
}
thufir#dur:~/tcl/packages$
thufir#dur:~/tcl/packages$ tclsh foo.tcl
can't find package tutstack 1.0
while executing
"package require tutstack 1.0"
(file "foo.tcl" line 1)
thufir#dur:~/tcl/packages$
to my understanding, I need to compile a list or map of where packages are.

The problem is that Tcl is not finding the index file (which should be called pkgIndex.tcl) for your package. If you had implemented the weather 1.0 package as a file weather.tcl, then you'd probably be looking to have an index file something like this in the same directory:
package ifneeded weather 1.0 [list source [file join $dir weather.tcl]]
That says “to load version 1.0 of the weather package, run this script” where the script is generated at runtime and binds $dir in (which is a variable always defined in the context where package index loader runs package ifneeded).
Once that's there, you need to allow Tcl to find the index file. This can be done by putting that directory or its immediate parent on the Tcl global auto_path list; either do that inside your script before you load any packages (very useful for applications that have internal packages) or you can initialise that from outside of Tcl too by setting the TCLLIBPATH environment variable. Note that the value of that variable is a Tcl list of directories, not a system path like env(PATH). This matters if you have backslashes or spaces in directory names, or if you want to have multiple elements on the list. Fortunately, you can usually avoid all of these issues in the case of adding a single directory as an environment variable, even on Windows, by using / instead of \ and by following usual installation practice and not putting a space in names. When adding a path during application launch it's easier: you just use lappend, perhaps like this (very early in your main script):
lappend auto_path [file join [file dirname [info script]] my_app_pacakges]
# If the script is in foo/bar.tcl then packages are in or below foo/my_app_packages

result which runs:
thufir#dur:~/tcl/foo$
thufir#dur:~/tcl/foo$ tree
.
├── api
│   ├── pkgIndex.tcl
│   └── tutstack.tcl
└── main.tcl
1 directory, 3 files
thufir#dur:~/tcl/foo$
thufir#dur:~/tcl/foo$ cat main.tcl
lappend auto_path /home/thufir/tcl/foo/api
package require tutstack 1.0
set stack [tutstack::create]
foreach num {1 2 3 4 5} { tutstack::push $stack $num }
while { ![tutstack::empty $stack] } {
puts "[tutstack::pop $stack]"
}
tutstack::destroy $stack
thufir#dur:~/tcl/foo$
thufir#dur:~/tcl/foo$ cat api/pkgIndex.tcl
# Tcl package index file, version 1.1
# This file is generated by the "pkg_mkIndex" command
# and sourced either when an application starts up or
# by a "package unknown" script. It invokes the
# "package ifneeded" command to set up package-related
# information so that packages will be loaded automatically
# in response to "package require" commands. When this
# script is sourced, the variable $dir must contain the
# full path name of this file's directory.
package ifneeded tutstack 1.0 [list source [file join $dir tutstack.tcl]]
thufir#dur:~/tcl/foo$
thufir#dur:~/tcl/foo$ cat api/tutstack.tcl
# Register the package
package provide tutstack 1.0
package require Tcl 8.5
# Create the namespace
namespace eval ::tutstack {
# Export commands
namespace export create destroy push pop peek empty
# Set up state
variable stack
variable id 0
}
# Create a new stack
proc ::tutstack::create {} {
variable stack
variable id
set token "stack[incr id]"
set stack($token) [list]
return $token
}
# Destroy a stack
proc ::tutstack::destroy {token} {
variable stack
unset stack($token)
}
# Push an element onto a stack
proc ::tutstack::push {token elem} {
variable stack
lappend stack($token) $elem
}
# Check if stack is empty
proc ::tutstack::empty {token} {
variable stack
set num [llength $stack($token)]
return [expr {$num == 0}]
}
# See what is on top of the stack without removing it
proc ::tutstack::peek {token} {
variable stack
if {[empty $token]} {
error "stack empty"
}
return [lindex $stack($token) end]
}
# Remove an element from the top of the stack
proc ::tutstack::pop {token} {
variable stack
set ret [peek $token]
set stack($token) [lrange $stack($token) 0 end-1]
return $ret
}
thufir#dur:~/tcl/foo$
thufir#dur:~/tcl/foo$ tclsh main.tcl
5
4
3
2
1
thufir#dur:~/tcl/foo$
generating the config file:
thufir#dur:~/tcl/foo/api$
thufir#dur:~/tcl/foo/api$ tclsh
%
%
% pkg_mkIndex . *.tcl
%
% exit
thufir#dur:~/tcl/foo/api$

Related

Nextflow name collision

I have files with identical names but in different folders. Nextflow stages these files into the same work directory resulting in name collisions. My question is how to deal with that without renaming the files. Example:
# Example data
mkdir folder1 folder2
echo 1 > folder1/file.txt
echo 2 > folder2/file.txt
# We read from samplesheet
$ cat samplesheet.csv
sample,file
sample1,/home/atpoint/foo/folder1/file.txt
sample1,/home/atpoint/foo/folder2/file.txt
# Nextflow main.nf
#! /usr/bin/env nextflow
nextflow.enable.dsl=2
// Read samplesheet and group files by sample (first column)
samplesheet = Channel
.fromPath(params.samplesheet)
.splitCsv(header:true)
.map {
sample = it['sample']
file = it['file']
tuple(sample, file)
}
ch_samplesheet = samplesheet.groupTuple(by:0)
// That creates a tuple like:
// [sample1, [/home/atpoint/foo/folder1/file.txt, /home/atpoint/foo/folder2/file.txt]]
// Dummy process that stages both files into the same work directory folder
process PRO {
input:
tuple val(samplename), path(files)
output:
path("out.txt")
script:
"""
echo $samplename with files $files > out.txt
"""
}
workflow { PRO(ch_samplesheet) }
# Run it
NXF_VER=21.10.6 nextflow run main.nf --samplesheet $(realpath samplesheet.csv)
...obviously resulting in:
N E X T F L O W ~ version 21.10.6
Launching `main.nf` [adoring_jennings] - revision: 87f26fa90b
[- ] process > PRO -
Error executing process > 'PRO (1)'
Caused by:
Process `PRO` input file name collision -- There are multiple input files for each of the following file names: file.txt
So, what now? The real world application here is sequencing replicates of the same fastq file, which then have the same name, but are in different folders, and I want to feed them into a process that merges them. I am aware of this section in the docs but cannot say that any of it was helpful or that I understand it properly.
You can use stageAs option in your process definition.
#! /usr/bin/env nextflow
nextflow.enable.dsl=2
samplesheet = Channel
.fromPath(params.samplesheet)
.splitCsv(header:true)
.map {
sample = it['sample']
file = it['file']
tuple(sample, file)
}
.groupTuple()
.set { ch_samplesheet }
// [sample1, [/path/to/folder1/file.txt, /path/to/folder2/file.txt]]
process PRO {
input:
tuple val(samplename), path(files, stageAs: "?/*")
output:
path("out.txt")
shell:
def input_str = files instanceof List ? files.join(" ") : files
"""
cat ${input_str} > out.txt
"""
}
workflow { PRO(ch_samplesheet) }
See an example from nf-core and the path input type docs

Nextflow - No such variable: prefix

I tried to run my nextflow script and the first two precess worded fine, but the third process Conbinevcf reported an error, showing that the variable prefix was not found.
process Annovar_genebased {
publishDir "${params.output}/annovar", mode: 'copy'
input:
path 'snp_anatation' from anatation1.flatMap()
val humandb
val refgene
output:
path "*.exonic_variant_function" into end
"""
prefix=\$(basename \$(readlink snp_anatation) .avinput)
perl $refgene -geneanno -dbtype refGene -out \${prefix}.anatation -buildver hg19 $snp_anatation $humandb -hgvs
rm *.log
rm *.variant_function
"""
}
process Annovar {
publishDir "${params.output}/annovar", mode: 'copy'
input:
path 'snp_anatation' from anatation2.flatMap()
val annovar_table
val humandb
output:
path "*.csv" into end1
"""
prefix=\$(basename \$(readlink snp_anatation) .avinput)
perl $annovar_table $snp_anatation $humandb -buildver hg19 -out \${prefix}.anatation -remove -protocol refGene,cytoBand,exac03,clinvar_20200316,gnomad211_exome -operation g,r,f,f,f -nastring . -csvout -polish
"""
}
I got stuck on this process
process Combinevcf {
publishDir "${params.output}/combinevcf", mode: 'copy'
input:
path 'genebased' from end.flatMap()
path 'allbased' from end1.flatMap()
output:
path "*_3.csv" into end3
"""
prefix=\$(basename \$(readlink genebased) .exonic_variant_function)
prefix1=\$(basename \$(readlink allbased) .csv)
cat ${prefix}.exonic_variant_function | tr -s ‘[:blank:]’ ‘,’ | awk 'BEGIN{FS=",";OFS="," }{ print \$3,\$13,\$22}' | awk ' BEGIN { OFS=", "; print "refGene", "refGene", "refGene", "refGene", "refGene", "Zogysity","chr", "filter" } { print \$0, "" } ' > ${prefix}_1.csv
awk 'BEGIN{FS=",";OFS="," }{ print \$1,\$2,\$3,\$4,\$5,\$6,\$7,\$8,\$9,\$10,\$15,\$21,\$24,\$25}' ${prefix1}.csv > ${prefix1}_2.csv
paste ${prefix}_1.csv ${prefix1}_2.csv > ${prefix}_3.csv
"""
}
I am not sure what went wrong, any help would be appreciated.
You need to escape your ${prefix} with backslashes to tell nextflow that the variable prefix is in the script block scope, and not in the nextflow scope.
See https://www.nextflow.io/docs/latest/process.html#script for more info:
Since Nextflow uses the same Bash syntax for variable substitutions in strings, you must manage them carefully depending on whether you want to evaluate a Nextflow variable or a Bash variable

jenkins on windows. Declarative pipeline jenkinsfile. how to set and get correct variable values

I use Jenkinsefile file to run the Stages.
It is in Jenkins pipeline installed on windows, Declarative pipeline.
On the begining I do:
pipeline {
agent { label 'master'}
environment {
My_build_result = 7
}
....
Than
stage('Test') {
steps {
echo 'Testing..'
bat """
cd Utils
"C:\\Program Files\\MATLAB\\R2019b\\bin\\matlab.exe" -wait -nodisplay -nosplash -nodesktop -r "run('automatic_tests\\run_test.m');"
echo %errorlevel%
set /a My_build_result_temp = %errorlevel%
set My_build_result = %My_build_result_temp%
"""
script {
My_build_result = bat(returnStatus:true , script: "exit (2)").trim()
echo "My_build_result ${env.My_build_result}"
if (My_build_result != 0) {
echo "inside if"
}
}
}
}
The variable My_build_result get value 7 at the begining
Inside the bat section, it suppose to get value 0 from %errorlevel%
Inside the script section it suppose to get value 2
BUT
in the echo "My_build_result ${env.My_build_result}" I get print of 7
(and it goes inside the if sentense)
How do I define variable that can be set value in bat"""
"""
and in script """
"""
section of the stage
and also be familiar in another stages and in the post { always { .. }} at the end ???
BTW: add env.before My_build_result (env.My_build_result ) does not work
Thanks a lot
In the first bat call, you are setting the environment variable only inside of the batch script environment. Environment variable values that are assigned through set don't persist when the script ends. Think of these like local variables. Simply use returnStatus: true to return the last value of ERRORLEVEL. There is no need to use %ERRORLEVEL% in the batch script here.
steps {
script {
My_build_result = bat returnStatus: true, script: """
cd Utils
"C:\\Program Files\\MATLAB\\R2019b\\bin\\matlab.exe" -wait -nodisplay -nosplash -nodesktop -r "run('automatic_tests\\run_test.m');"
"""
// My_build_result now has the value of ERRORLEVEL from the last command
// called in the batch script.
}
}
In the 2nd bat call the 1st mistake is to call the trim() method. Result type of bat step is Integer, when returnStatus: true is passed. The trim() method is only available when returnStdout: true is passed in which case the result type would be String. The 2nd mistake is to use brackets around the exit code value. The fixed code should look like:
My_build_result = bat returnStatus: true, script: "exit 2"
// My_build_result now equals 2

How to manage module dependencies in jq

Since version 1.5 the jq data processing language has a library module system. A module consists of optional metadata and a set of functions. For instance
module { name: "util", version: "1.0.0" };
def digitsum: tostring|split("")|map(tonumber)|add;
stored as file util.jq can be used like this:
$ echo '789' | jq -L. 'include "util"; digitsum'
24
Modules can use other modules and the dependencies are tracked by the modulemeta directive but how to express and check for a minimum version of a module number? For instance:
module {
name: "math",
version: "0.1.0",
};
include "util"; # TODO: require at least version 1.0.0!
def digitroot:
(.|digitsum) as $sum |
if $sum<10 then $sum else $sum|digitroot end;
The support for modules in jq is currently (June 2019) still very minimal, though on github there is a module management system for jq: https://github.com/joelpurra/jqnpm
Without using such an external module management system, what can be done in jq itself? Extending the given example, the following illustrates one approach to supporting version requirements. Notice the additional key named dependencies in the metadata of the math module. (Currently, this key cannot be named deps as jq overwrites it.)
Files
dependencies.jq
# Recursively check specified version constraints
module { name: "dependencies", version: "0.0.2" };
# parents of a module as defined by its .deps
def parents:
. as $in
| if type == "array" then map(parents) | add
else modulemeta | .deps | map(.relpath)
end ;
# ancestors of a single module or an array of modules.
# The array of "ancestors" of a module includes itself.
def ancestors:
# input and $visited should be arrays of strings
def ancestors($visited):
. as $in
| ($in - $visited) as $new
| if $new == [] then $visited
else $new | parents | ancestors($visited + $new | unique)
end;
if type == "array" then . else [.] end
| ancestors([]) ;
def versionsort:
def parse:
sub("(?<a>(alpha|beta|gamma))"; "\(.a).")
| [splits("[-.]")]
| map(tonumber? // .) ;
sort_by(parse);
# Input: a module name
# Emit empty if the constraints for the given module are satisfied, otherwise raise an error
def dependencies($version):
def le($y): (. == $y) or ([.,$y] | . == versionsort);
modulemeta
| .version as $mv
| if (($mv == null) or ($version | le($mv))) then empty
else ("module \(.name) dependencies version \($version) vs \($mv)" | error)
end ;
# Input: a module name or array of module names
# Check the module-version dependencies in .dependencies, proceeding up the chain as defined by .deps
def dependencies:
def check:
modulemeta
| select(has("dependencies"))
| all( .dependencies | to_entries[];
.key as $m | .value as $v | ($m | dependencies($v) ))
| empty;
ancestors[] | check;
util.jq
module { name: "util", version: "1.0.0" };
def digitsum: tostring|split("")|map(tonumber)|add;
math.jq
module {
name: "math",
version: "0.1.0",
dependencies: {"util": "1.0.0"} };
include "util" ;
def digitroot:
digitsum as $sum
| if $sum<10 then $sum
else $sum|digitroot
end;
Invocation
jq -n -L . '
include "dependencies";
include "math";
"math" | dependencies,
(123|digitroot) '

Command line tool to dump Windows DLL version?

I need a command line tool to dump standard Windows DLL version info so I can process it by means of a bash script (Cygwin).
As a Java developer I am not very used to Microsoft development tools (though I have a little bit of experience with Microsoft Visual Embedded C++ 4.0 and Microsoft Visual Basic 6.0).
The appropriate tool seems to be mt.exe, as stated on SO. However the only chance I have found to get this little application is to download a 1.29 GB ISO of the Windows SDK for Windows Server 2008 and .NET Framework. I can not believe this is the only way to do it.
I have also found a little application on the Internet called PEView, but it displays too much (and useless in my case) information and it is not a command line application.
Standard objdump bundled inside Cygwin can also dump some information about the DLL files, but I can not see the option to dump DLL version. Note that MajorImageVersion, MinorImageVersion and other fields dumped by this tool (with -p option) are not related to own DLL version.
Any alternatives about what to do? Maybe I missed some important objdump option? Is mt.exe my only choice? If this is the case, is it possible to get it separately from Windows SDK?
You can use PowerShell to get the information you want.
(Get-Item C:\Path\To\MyFile.dll).VersionInfo
By default this will display ProductVersion and FileVersion
But the full VERSIONINFO is available. I.e. to return Comments
(Get-Item C:\Path\To\MyFile.dll).VersionInfo.Comments
Use Microsoft Sysinternals Sigcheck.
This sample outputs just the version:
sigcheck -q -n foo.dll
Unpacked sigcheck.exe is only 228 KB.
You can write a VBScript script to get the file version info:
VersionInfo.vbs
set args = WScript.Arguments
Set fso = CreateObject("Scripting.FileSystemObject")
WScript.Echo fso.GetFileVersion(args(0))
Wscript.Quit
You can call this from the command line like this:
cscript //nologo VersionInfo.vbs C:\Path\To\MyFile.dll
C:\>wmic datafile where name="C:\\Windows\\System32\\kernel32.dll" get version
Version
6.1.7601.18229
You can also look at filever.exe, which can be downloaded as part of the Windows XP SP2 Support Tools package - only 4.7MB of download.
or you can build one yourself. Open VS, create a new console application. Create a simple project with no ATL or MFC support, leave the stdafx option checked but do not check 'empty project' and call it VersionInfo.
You'll get a simple project with 2 files: VersionInfo.cpp and VersionInfo.h
Open the cpp file and paste the following into it, then compile. You'll be able to run it, first argument is the full filename, it'll print out "Product: 5.6.7.8 File: 1.2.3.4" based on the Version resource block. If there's no version resource it'll return -1, otherwise 0.
Compiles to an 8k binary using the dll CRT, 60k with everything linked statically (set in the C++ options, change "Code Generation page, Runtime options" to "/MT")
HTH.
PS. If you don't want to use Visual Studio, it'll still compile using any c++ compiler (fingers crossed), but you'll almost certainly have to change the #pragma - just specify that lib in the linker settings instead, the pragma's just a shorthand to automatically link with that library.
// VersionInfo.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#pragma comment(lib, "version.lib")
int _tmain(int argc, _TCHAR* argv[])
{
DWORD handle = 0;
DWORD size = GetFileVersionInfoSize(argv[1], &handle);
BYTE* versionInfo = new BYTE[size];
if (!GetFileVersionInfo(argv[1], handle, size, versionInfo))
{
delete[] versionInfo;
return -1;
}
// we have version information
UINT len = 0;
VS_FIXEDFILEINFO* vsfi = NULL;
VerQueryValue(versionInfo, L"\\", (void**)&vsfi, &len);
WORD fVersion[4], pVersion[4];
fVersion[0] = HIWORD(vsfi->dwFileVersionMS);
fVersion[1] = LOWORD(vsfi->dwFileVersionMS);
fVersion[2] = HIWORD(vsfi->dwFileVersionLS);
fVersion[3] = LOWORD(vsfi->dwFileVersionLS);
pVersion[0] = HIWORD(vsfi->dwProductVersionMS);
pVersion[1] = LOWORD(vsfi->dwProductVersionMS);
pVersion[2] = HIWORD(vsfi->dwProductVersionLS);
pVersion[3] = LOWORD(vsfi->dwProductVersionLS);
printf("Product: %d.%d.%d.%d File: %d.%d.%d.%d\n",
pVersion[0], pVersion[1],
pVersion[2], pVersion[3],
fVersion[0], fVersion[1],
fVersion[2], fVersion[3]);
delete[] versionInfo;
return 0;
}
Using Powershell it is possible to get just the Version string, i.e. 2.3.4 from any dll or exe with the following command
(Get-Item "C:\program files\OpenVPN\bin\openvpn.exe").VersionInfo.ProductVersion
Tested on Windows 10
The listdlls tools from Systernals might do the job: http://technet.microsoft.com/en-us/sysinternals/bb896656.aspx
listdlls -v -d mylib.dll
This function returns the ntfs Windows file details for any file using Cygwin bash (actual r-click-properties-info) to the term
Pass the files path to finfo(), can be unix path, dos path, relative or absolute. The file is converted into an absolute nix path, then checked to see if it is in-fact a regular/existing file. Then converted into an absolute windows path and sent to "wmic". Then magic, you have windows file details right in the terminal. Uses: cygwin, cygpath, sed, and awk. Needs Windows WMI "wmic.exe" to be operational. The output is corrected for easy...
$ finfo notepad.exe
$ finfo "C:\windows\system32\notepad.exe"
$ finfo /cygdrive/c/Windows/System32/notepad.exe
$ finfo "/cygdrive/c/Program Files/notepad.exe"
$ finfo ../notepad.exe
finfo() {
[[ -e "$(cygpath -wa "$#")" ]] || { echo "bad-file"; return 1; }
echo "$(wmic datafile where name=\""$(echo "$(cygpath -wa "$#")" | sed 's/\\/\\\\/g')"\" get /value)" |\
sed 's/\r//g;s/^M$//;/^$/d' | awk -F"=" '{print $1"=""\033[1m"$2"\033[0m" }'
}
There is an command line application called "ShowVer" at CodeProject:
ShowVer.exe command-line VERSIONINFO display program
As usual the application comes with an exe and the source code (VisualC++ 6).
Out outputs all the meta data available:
On a German Win7 system the output for user32.dll is like this:
VERSIONINFO for file "C:\Windows\system32\user32.dll": (type:0)
Signature: feef04bd
StrucVersion: 1.0
FileVersion: 6.1.7601.17514
ProductVersion: 6.1.7601.17514
FileFlagsMask: 0x3f
FileFlags: 0
FileOS: VOS_NT_WINDOWS32
FileType: VFT_DLL
FileDate: 0.0
LangID: 040704B0
CompanyName : Microsoft Corporation
FileDescription : Multi-User Windows USER API Client DLL
FileVersion : 6.1.7601.17514 (win7sp1_rtm.101119-1850)
InternalName : user32
LegalCopyright : ® Microsoft Corporation. Alle Rechte vorbehalten.
OriginalFilename : user32
ProductName : Betriebssystem Microsoft« Windows«
ProductVersion : 6.1.7601.17514
Translation: 040704b0
For some DLLs I found version info manually with objdump in .rsrc. With objdump -s -j .rsrc zlib1.dll:
zlib1.dll: file format pei-x86-64
Contents of section .rsrc:
62e9e000 00000000 00000000 00000000 00000100 ................
62e9e010 10000000 18000080 00000000 00000000 ................
62e9e020 00000000 00000100 01000000 30000080 ............0...
62e9e030 00000000 00000000 00000000 00000100 ................
62e9e040 09040000 48000000 58e00100 34030000 ....H...X...4...
62e9e050 00000000 00000000 34033400 00005600 ........4.4...V.
62e9e060 53005f00 56004500 52005300 49004f00 S._.V.E.R.S.I.O.
62e9e070 4e005f00 49004e00 46004f00 00000000 N._.I.N.F.O.....
62e9e080 bd04effe 00000100 02000100 00000b00 ................
62e9e090 02000100 00000b00 3f000000 00000000 ........?.......
62e9e0a0 04000000 02000000 00000000 00000000 ................
62e9e0b0 00000000 94020000 01005300 74007200 ..........S.t.r.
62e9e0c0 69006e00 67004600 69006c00 65004900 i.n.g.F.i.l.e.I.
62e9e0d0 6e006600 6f000000 70020000 01003000 n.f.o...p.....0.
62e9e0e0 34003000 39003000 34004500 34000000 4.0.9.0.4.E.4...
62e9e0f0 64001e00 01004600 69006c00 65004400 d.....F.i.l.e.D.
62e9e100 65007300 63007200 69007000 74006900 e.s.c.r.i.p.t.i.
62e9e110 6f006e00 00000000 7a006c00 69006200 o.n.....z.l.i.b.
62e9e120 20006400 61007400 61002000 63006f00 .d.a.t.a. .c.o.
62e9e130 6d007000 72006500 73007300 69006f00 m.p.r.e.s.s.i.o.
62e9e140 6e002000 6c006900 62007200 61007200 n. .l.i.b.r.a.r.
62e9e150 79000000 2e000700 01004600 69006c00 y.........F.i.l.
62e9e160 65005600 65007200 73006900 6f006e00 e.V.e.r.s.i.o.n.
62e9e170 00000000 31002e00 32002e00 31003100 ....1...2...1.1.
62e9e180 00000000 34000a00 01004900 6e007400 ....4.....I.n.t.
62e9e190 65007200 6e006100 6c004e00 61006d00 e.r.n.a.l.N.a.m.
62e9e1a0 65000000 7a006c00 69006200 31002e00 e...z.l.i.b.1...
62e9e1b0 64006c00 6c000000 7c002c00 01004c00 d.l.l...|.,...L.
62e9e1c0 65006700 61006c00 43006f00 70007900 e.g.a.l.C.o.p.y.
62e9e1d0 72006900 67006800 74000000 28004300 r.i.g.h.t...(.C.
62e9e1e0 29002000 31003900 39003500 2d003200 ). .1.9.9.5.-.2.
62e9e1f0 30003100 37002000 4a006500 61006e00 0.1.7. .J.e.a.n.
62e9e200 2d006c00 6f007500 70002000 47006100 -.l.o.u.p. .G.a.
62e9e210 69006c00 6c007900 20002600 20004d00 i.l.l.y. .&. .M.
62e9e220 61007200 6b002000 41006400 6c006500 a.r.k. .A.d.l.e.
62e9e230 72000000 3c000a00 01004f00 72006900 r...<.....O.r.i.
62e9e240 67006900 6e006100 6c004600 69006c00 g.i.n.a.l.F.i.l.
62e9e250 65006e00 61006d00 65000000 7a006c00 e.n.a.m.e...z.l.
62e9e260 69006200 31002e00 64006c00 6c000000 i.b.1...d.l.l...
62e9e270 2a000500 01005000 72006f00 64007500 *.....P.r.o.d.u.
62e9e280 63007400 4e006100 6d006500 00000000 c.t.N.a.m.e.....
62e9e290 7a006c00 69006200 00000000 32000700 z.l.i.b.....2...
62e9e2a0 01005000 72006f00 64007500 63007400 ..P.r.o.d.u.c.t.
62e9e2b0 56006500 72007300 69006f00 6e000000 V.e.r.s.i.o.n...
62e9e2c0 31002e00 32002e00 31003100 00000000 1...2...1.1.....
62e9e2d0 78003000 01004300 6f006d00 6d006500 x.0...C.o.m.m.e.
62e9e2e0 6e007400 73000000 46006f00 72002000 n.t.s...F.o.r. .
62e9e2f0 6d006f00 72006500 20006900 6e006600 m.o.r.e. .i.n.f.
62e9e300 6f007200 6d006100 74006900 6f006e00 o.r.m.a.t.i.o.n.
62e9e310 20007600 69007300 69007400 20006800 .v.i.s.i.t. .h.
62e9e320 74007400 70003a00 2f002f00 77007700 t.t.p.:././.w.w.
62e9e330 77002e00 7a006c00 69006200 2e006e00 w...z.l.i.b...n.
62e9e340 65007400 2f000000 44000000 01005600 e.t./...D.....V.
62e9e350 61007200 46006900 6c006500 49006e00 a.r.F.i.l.e.I.n.
62e9e360 66006f00 00000000 24000400 00005400 f.o.....$.....T.
62e9e370 72006100 6e007300 6c006100 74006900 r.a.n.s.l.a.t.i.
62e9e380 6f006e00 00000000 0904e404 00000000 o.n.............
and one way with makecab:
; #echo off
;;goto :end_help
;;setlocal DsiableDelayedExpansion
;;;
;;;
;;; fileinf /l list of full file paths separated with ;
;;; fileinf /f text file with a list of files to be processed ( one on each line )
;;; fileinf /? prints the help
;;;
;;:end_help
; REM Creating a Newline variable (the two blank lines are required!)
; set NLM=^
; set NL=^^^%NLM%%NLM%^%NLM%%NLM%
; if "%~1" equ "/?" type "%~f0" | find ";;;" | find /v "find" && exit /b 0
; if "%~2" equ "" type "%~f0" | find ";;;" | find /v "find" && exit /b 0
; setlocal enableDelayedExpansion
; if "%~1" equ "/l" (
; set "_files=%~2"
; echo !_files:;=%NL%!>"%TEMP%\file.paths"
; set _process_file="%TEMP%\file.paths"
; goto :get_info
; )
; if "%~1" equ "/f" if exist "%~2" (
; set _process_file="%~2"
; goto :get_info
; )
; echo incorect parameters & exit /b 1
; :get_info
; set "file_info="
; makecab /d InfFileName=%TEMP%\file.inf /d "DiskDirectory1=%TEMP%" /f "%~f0" /f %_process_file% /v0>nul
; for /f "usebackq skip=4 delims=" %%f in ("%TEMP%\file.inf") do (
; set "file_info=%%f"
; echo !file_info:,=%nl%!
; )
; endlocal
;endlocal
; del /q /f %TEMP%\file.inf 2>nul
; del /q /f %TEMP%\file.path 2>nul
; exit /b 0
.set DoNotCopyFiles=on
.set DestinationDir=;
.set RptFileName=nul
.set InfFooter=;
.set InfHeader=;
.Set ChecksumWidth=8
.Set InfDiskLineFormat=;
.Set Cabinet=off
.Set Compress=off
.Set GenerateInf=ON
.Set InfDiskHeader=;
.Set InfFileHeader=;
.set InfCabinetHeader=;
.Set InfFileLineFormat=",file:*file*,date:*date*,size:*size*,csum:*csum*,time:*time*,vern:*ver*,vers:*vers*,lang:*lang*"
example output (it has a string version which is a small addition to wmic method :) ):
c:> fileinfo.bat /l C:\install.exe
file:install.exe
date:11/07/07
size:562688
csum:380ef239
time:07:03:18a
vern:9.0.21022.8
vers:9.0.21022.8 built by: RTM
lang:1033
and one more Using shell.application and hybrid batch\jscript.Here's tooptipInfo.bat :
#if (#X)==(#Y) #end /* JScript comment
#echo off
rem :: the first argument is the script name as it will be used for proper help message
cscript //E:JScript //nologo "%~f0" %*
exit /b %errorlevel%
#if (#X)==(#Y) #end JScript comment */
//////
FSOObj = new ActiveXObject("Scripting.FileSystemObject");
var ARGS = WScript.Arguments;
if (ARGS.Length < 1 ) {
WScript.Echo("No file passed");
WScript.Quit(1);
}
var filename=ARGS.Item(0);
var objShell=new ActiveXObject("Shell.Application");
/////
//fso
ExistsItem = function (path) {
return FSOObj.FolderExists(path)||FSOObj.FileExists(path);
}
getFullPath = function (path) {
return FSOObj.GetAbsolutePathName(path);
}
//
//paths
getParent = function(path){
var splitted=path.split("\\");
var result="";
for (var s=0;s<splitted.length-1;s++){
if (s==0) {
result=splitted[s];
} else {
result=result+"\\"+splitted[s];
}
}
return result;
}
getName = function(path){
var splitted=path.split("\\");
return splitted[splitted.length-1];
}
//
function main(){
if (!ExistsItem(filename)) {
WScript.Echo(filename + " does not exist");
WScript.Quit(2);
}
var fullFilename=getFullPath(filename);
var namespace=getParent(fullFilename);
var name=getName(fullFilename);
var objFolder=objShell.NameSpace(namespace);
var objItem=objFolder.ParseName(name);
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb787870(v=vs.85).aspx
WScript.Echo(fullFilename + " : ");
WScript.Echo(objFolder.GetDetailsOf(objItem,-1));
}
main();
used against cmd.exe :
C:\Windows\System32\cmd.exe :
File description: Windows Command Processor
Company: Microsoft Corporation
File version: 6.3.9600.16384
Date created: ?22-?Aug-?13 ??13:03
Size: 347 KB