Using wildcard for files with jq on Windows

北战南征 提交于 2021-01-28 05:31:01

问题


I am using jq 1.6 on Windows 8.1 and facing same issue as reported here https://github.com/stedolan/jq/issues/1644

Command as simple as jq . *.json is failing with following error:

Assertion failed!

Program: jq.exe File: src/main.c, Line 256 Expression: wargc == argc

This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.

Does anyone have solution to this? What is the correct way to use jq with all the files in the folder on windows?


回答1:


This is a curious problem.

As alredy answered by peak, cmd.exe does not expand wildcards, leaving this work to the programs. And jq does not handle wildcards (from the issues list, more later).

But this is not the full reason for this failure.

As the question points, source code fails in an assert: wargc == argc. When reading the source code, in windows jq tries to process the original command line with

 wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &wargc);

trying to retrieve the equivalent to argv[] and argc but handling multibyte arguments.

As cmd is not expanding the wildcard there will be three arguments (command line in question)

jq  .  *.json
^^  ^  ^....^  
0   1  2      

both in argv[] and wargv[], so argc and wargc should match.

Then, why does it fail? Why argc is different to wargc?

Because GCC was used to compile the program.

And no, the problem is not GCC itself. The "problem" is that the argument handling in GCC runtime DOES expand the wildcard (Microsoft compiler runtime does not, but it doesn't matter as it will also not solve the problem).

That means that argc and argv (determined by GCC code with wildcard expansion) will contain information according to the number of files matching the wildcard while wargc and wargv (determined by MS code without wildcard expansion) will not.

A simple way to probe it is to have only one .json file when trying the previous command. The assert will not fail but jq will fail with jq: error: Could not open file *.json: Invalid argument as it does not handle wildcards.

jq . test.json       As seen in argv   argc  = 3
jq . *.json          As seen in wargv  wargc = 3

So, how to deal with it? Without modifying the jq's source code your best option is to concatenate the list of files and pass it to jq. References in peak's answer and in your comment should deal with the problem.

But remember that in cmd and batch files your command lines are limited to 8191 characters. If it is not enough to deal with your problem you can try with something like (yes, a lot of lines, most of them comments and command usage)

@if (@this==@isBatch) @then /* ------------------------------------- batch code
@echo off
    setlocal enableextensions disabledelayedexpansion

    rem This is an hybrid batch/javascript file. The batch part will retrieve
    rem the required information and start the cscript engine to execute the 
    rem javascript part of this file.

    rem Retrieve a safe reference to current batch file 
    call :getCurrentFile thisFile fileName

    rem Arguments to current batch file are the command line to execute later
    rem Using an environment variable to avoid losing quotes when using the
    rem internal Wscript argumeng handling routines
    set [commandLine]=%*

    if not defined [commandLine] (
        echo(
        echo usage: command1 ^| "%fileName%" command2
        echo(
        echo where:
        echo     command1   is a command generating a set of lines that will be
        echo                concatenated to pass as arguments to command2
        echo(
        echo     command2   is the command to execute with all the lines from 
        echo                command1 as command line arguments
        echo(
        echo examples:
        echo(
        echo    dir /b ^| "%fileName%" cmd /c echo
        echo    dir /b *.json ^| "%fileName%" jq . 
        echo(
        goto :eof
    )

    rem Execute the javascript part of this script
    "%windir%\system32\cscript.exe" //nologo //e:JScript "%thisFile%" 
    goto :eof


:getCurrentFile fullPath fileName
    set "%~1=%~f0"
    set "%~2=%~nx0"
    goto :eof

------------------------------------------------------------- end of batch code
*/@end //------------------------------------------------------ javascript code

/*
    This script will read all lines from standard input and execute the 
    command stored by the batch code above into the [commandLine] environment 
    variable, passing as command lien arguments the concatenation of all lines 
    present in the standard input.
*/

var shell = WScript.CreateObject('WScript.Shell')
  , commandLine = shell.Environment("PROCESS").Item('[commandLine]')
  , stdIn = WScript.StdIn
  , stdOut = WScript.StdOut
  , stdErr = WScript.StdErr
  , line = ''
  , buffer = []
  ;
    // read the list of arguments from standard input
    while ( !stdIn.AtEndOfStream ){ 
        if ( 
            line = stdIn.ReadLine().replace(/"/g, '') 
        ) buffer.push( ' "' + line + '"' );
    };

    // concatenate all arguments 
    buffer = buffer.join('');

    // if we don't have a command line, output what we have contatenated 
    // but if we have a command line, execute it, get its output and show it
    // as it is possible that we are piping it to another process.

    if ( commandLine ){
        try {
            stdOut.WriteLine(
                shell.Exec( commandLine + buffer ).StdOut.ReadAll()
            );
        } catch ( e ){
            stdErr.WriteLine( 'ERROR: Command line exec failed when running:' );
            stdErr.WriteLine( '---------------------------------------------' );
            stdErr.WriteLine( commandLine + buffer );
            stdErr.WriteLine( '---------------------------------------------' );
        };
    } else {
        stdOut.WriteLine( buffer );
    };

Save it as a cmd file (ex. list2args.cmd) and use it as suggested

dir /b *.json | list2args.cmd jq .

The difference is that doing the concatenation inside the script part and starting the process using the WScript.Shell.Exec method we can use command lines up to 32KB (the windows limit for command lines).




回答2:


As explained at https://en.wikibooks.org/wiki/Windows_Batch_Scripting

"Unlike shells of some other operating systems, the cmd.exe shell does not perform wildcard expansion"

So assuming you can't simply process the files one at a time, you'll either have to create the list of files explicitly, or use a different shell.

For further details and suggestions, see https://superuser.com/questions/460598/is-there-any-way-to-get-the-windows-cmd-shell-to-expand-wildcard-paths

and if you’re using Windows 10:

https://www.howtogeek.com/249966/how-to-install-and-use-the-linux-bash-shell-on-windows-10/



来源:https://stackoverflow.com/questions/53387811/using-wildcard-for-files-with-jq-on-windows

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!