chrisant996 / clink

Bash's powerful command line editing in cmd.exe
https://chrisant996.github.io/clink/
GNU General Public License v3.0
3.66k stars 144 forks source link

doskey macros not working in pipes #667

Closed iamqiz closed 2 months ago

iamqiz commented 2 months ago

Any macro that comes after the pipe symbol (|) in a command line does not work for example

C:\Users\pcmsi\tmp>doskey test1=echo a $1

C:\Users\pcmsi\tmp>test1 123
a 123

C:\Users\pcmsi\tmp>echo 353 |test1
a

I want it to output "a 353"

ps: clink document (https://chrisant996.github.io/clink/clink.html) say doskey.enhanced is true by default, but my clink's doskey.enhanced is false

chrisant996 commented 2 months ago

ps: clink document (https://chrisant996.github.io/clink/clink.html) say doskey.enhanced is true by default, but my clink's doskey.enhanced is false

The doskey.enhanced setting is indeed true by default (refer to the source code here).

Somehow must have gotten turned off. Maybe you turned it off in the past.

Have you tried turning it back on?

chrisant996 commented 2 months ago

P.S. If you believe you never turned it off, please share your clink_settings file.

iamqiz commented 2 months ago

my C:\Users\pcmsi\AppData\Local\clink\clink_settings have following lines :

# name: Add enhancements to Doskey
# type: boolean
doskey.enhanced = False

I turnd it on before i post this issue, but the doskey still does not work after pipe , see above

chrisant996 commented 2 months ago

I finally understood what you're saying.

C:\Users\pcmsi\tmp>doskey test1=echo a $1

C:\Users\pcmsi\tmp>test1 123
a 123

C:\Users\pcmsi\tmp>echo 353 |test1
a

I want it to output "a 353"

You have a misunderstanding about how pipes work. This isn't related to doskey aliases or to Clink.

You are trying to pipe the output from echo 353 into echo a. But the echo command does not read stdin, it only writes to stdout. You cannot pipe into echo.

Read about pipes to learn more about how they work and when to use them.

iamqiz commented 2 months ago

@chrisant996 Oh,thanks, You inspired me xargs can do that:

>C:\APP\Git\usr\bin\echo.exe 456|xargs   -I {} echo 123 {} 789
123 456 789

>doskey test1=xargs   -I {} echo 123 {} 789

>C:\APP\Git\usr\bin\echo.exe 456|test1
123 456 789

but using windows echo has some issue:

>echo 456|test1
 789456

😂

chrisant996 commented 2 months ago

Which xargs for Windows are you using? It seems likely it's an issue in whatever xargs implementation is being used, not in echo.

Since you were able to omit the cmd /c before echo, I would guess you are using the aaronator10 version, which is a batch script and therefore should be expected to have various limitations and problems.

When I use wargs the output order is correct. But you have to explicitly specify cmd /c to be able to invoke cmd internal commands such as echo.

(If you don't have to specify cmd /c when invoking cmd internal commands then every time the xargs tool must be prepending cmd /c in front of whatever command you give it, and that makes it slows as well as leading to issues in certain special cases.)

iamqiz commented 2 months ago

@chrisant996 I am using xargs from Git for Windows (https://git-scm.com/download/win)

chrisant996 commented 2 months ago

Definitely some kind of bug(s) in the xargs from Git for Windows. The same problem happens regardless whether a doskey alias is used.

image

image

The only output that matches expectations on Windows is the 123 456 789 (two spaces after "456") produced by piping through wargs.

EDIT: The only command that really matches expectations is the echo 456|wargs -I {} cmd /c echo 123 {} 789 command. The reason wargs adds quotes is to ensure the replacement is interpreted as a single argument. And that is consistent with how xargs works on nix, except that it's less obvious because on nix xargs uses \ escaping instead of quoting. I.e. echo file1 file2 | xargs -i cat {} executes cat file1\ file2 and tries to cat a single file named file1 file2. The only way to accomplish the same semantic behavior on Windows is to surround the replacement with quotes.

I don't know exactly what xargs is doing wrong that garbles echo 456 | xargs -I {} 123 {} 789 into 789456, but it's clearly the xargs program that's malfunctioning. And it garbles the output regardless whether a doskey alias is used (see screenshot above).

EDIT: I do know, now: It turns out xargs can't handle Windows line endings. It's only compatible with output from other *nix programs, not Windows programs.

[!NOTE] Explanation about the 123 456 789 output versus 123 456 789 output (two spaces after "456"):

I did some testing on Ubuntu in WSL and found that echo 456 | xargs -I {} 123 {} 789 produces 123 456 789 because on *nix the echo command strips trailing spaces. If you run echo '456 ' | xargs -I {} 123 {} 789 then it produces 123 456 789.

The difference in spaces is not coming from xargs, it's coming from the command used to write output into the pipe.

And on Windows the cmd echo command does not strip trailing spaces. So echo 456|wargs -I {} echo 123 {} 789 produces 123 456 789. And echo 456 | wargs -I {} echo 123 {} 789 produces 123 456 789.

chrisant996 commented 2 months ago

Aha: here's specifically what's going wrong in xargs...

The output from xargs is actually 123 456 \r 789. Which ends up looking like 789456 because xargs handled line endings wrong when reading from stdin, and it interpreted \r (Carriage Return) as being part of the input, instead of as part of the line ending to be stripped.

The problem is that the xargs program requires Unix line endings even when its used on Windows. Which makes it incompatible with output from most Windows programs -- it'll only work as expected when the output comes from another cygwin/msys2 Unix tool like the ones in the GNU tools that come with git for Windows.

iamqiz commented 2 months ago

@chrisant996

The output from xargs is actually 123 456 \r 789. Which ends up looking like 789456

good job! you answered my question! wargs is good tool to handle the line ending difference, thanks !