Skip to content

add current command-line to 'command_history' header entry#962

Merged
jdtournier merged 3 commits intotag_0.3.16from
store_command_history_in_header
Apr 21, 2017
Merged

add current command-line to 'command_history' header entry#962
jdtournier merged 3 commits intotag_0.3.16from
store_command_history_in_header

Conversation

@jdtournier
Copy link
Copy Markdown
Member

as suggested in #956.

All up for discussion. It's a simple addition, so why not. But it's not perfect, that history won't survive conversion to other formats - although it might (with a few modifications) work well with the MGH formats.

@Lestropie
Copy link
Copy Markdown
Member

Originally raised in #183.

Thought: Should the most recent command be the first line in command_history, rather than the last? That way if there's a long history, e60e9ba will make sure it still appears in the mrinfo output.

@thijsdhollander
Copy link
Copy Markdown
Contributor

Makes sense that, if you were interested in one command, of all commands, that would indeed probably be the most recent one. Another solution, rather than inverting the order (which has the potential to be non-intuitive in other scenarios), would be to let mrinfo show something like the first and last 3 lines (with a "..." in between) of any entry. Potential bonus: in the absence of conversion to non-supporting formats, it would give a good impression of what "part of a pipeline" has been applied to an image (i.e. first few until last few commands)... if that makes sense.

@jdtournier
Copy link
Copy Markdown
Member Author

Another solution, rather than inverting the order (which has the potential to be non-intuitive in other scenarios), would be to let mrinfo show something like the first and last 3 lines (with a "..." in between) of any entry.

I like this idea! You've probably noticed that my recent commit on tag_0.3.16 (e60e9ba) now prints the first 5 lines of any property, and cuts it short with a [ + N more ] line if it's longer than that (it also adds the -all option to prevent this truncation). It would make sense to instead show the first & last 2 or 3, with the [ + N more ] in the middle. I think that would address the issue?

The other thing to worry about if we reverse the order of the history is to ensure it's still consistent with the MGH format. I assume these entries appear in chronological order in the file, but that is admittedly only an assumption...

@jdtournier
Copy link
Copy Markdown
Member Author

You've probably noticed that my recent commit on tag_0.3.16 (e60e9ba) now prints the first 5 lines of any property

😁 of course you already have... Sorry, should have re-read your post before jumping in.

@ansk
Copy link
Copy Markdown

ansk commented Apr 11, 2017

In MGH format the command line history is chronological, not reverse. See the example of filled.mgz file which has 9 command line history entries. The tags also contain other various useful information, such as software and platform version.

TR: 2300ms
Flip: 10deg
TE: 4.63ms
TI: 900ms
[MGH TAG 31]: /hydradb/hydra_io/vypocty/freeSurfer/v6.1beta/CANEF/A_in_progress/skoch/ESO_P00007_20100611_1217_1_base/mri/transforms/talairach.xfm
[MGH TAG 33]: AutoAlign   1.000000   0.000000   0.000000   0.000000   0.000000   1.000000   0.000000   0.000000   0.000000   0.000000   1.000000   0.000000   0.000000   0.000000   0.000000   1.000000
[MGH TAG 41]: UNKNOWN
[MGH TAG 3]: mri_convert /tmpcount/freeSurfer/A_input/skochv/export_20_4_2016/ESO_P00007_20100611_1217_1/0006_t1_sag_mpr_3Diso__1mm/MR.1.3.12.2.1107.5.2.32.35226.20100611124804148709128 /tmpcount/freeSurfer/A_results/A_in_progress/skochv/pat/ESO_P00007_20100611_1217_1/mri/orig/001.mgz ProgramVersion: $Name: stable5 $  TimeStamp: 2016/04/21-13:32:39-GMT  BuildTimeStamp: May 13 2013 16:24:28  CVS: $Id: mri_convert.c,v 1.179.2.7 2012/09/05 21:55:16 mreuter Exp $  User: skochv  Machine: matpcp.ad.nudz.cz  Platform: Linux  PlatformVersion: 3.2.0-4-amd64  CompilerName: GCC  CompilerVersion: 40400  
[MGH TAG 3]: mri_convert.bin /hydradb/hydra_io/vypocty/freeSurfer/v6.1beta/CANEF/A_in_progress/skoch/ESO_P00007_20100611_1217_1/mri/rawavg.mgz /hydradb/hydra_io/vypocty/freeSurfer/v6.1beta/CANEF/A_in_progress/skoch/ESO_P00007_20100611_1217_1/mri/orig.mgz --conform ProgramVersion: $Name:  $  TimeStamp: 2017/02/13-09:32:22-GMT  BuildTimeStamp: Feb  6 2017 17:11:52  CVS: $Id: mri_convert.c,v 1.226 2016/02/26 16:15:24 mreuter Exp $  User: skoch  Machine: sup4.ad.nudz.cz  Platform: Linux  PlatformVersion: 3.16.0-4-amd64  CompilerName: GCC  CompilerVersion: 40400  
[MGH TAG 3]: mri_ca_normalize -c ctrl_pts.mgz -mask brainmask.mgz nu.mgz /hydra-db/hydra_io/vypocty/skoch/bin/freesurfer_dev_20170208/average/RB_all_2016-05-10.vc700.gca transforms/talairach.lta norm.mgz ProgramVersion: $Name:  $  TimeStamp: 2017/03/19-00:40:49-GMT  BuildTimeStamp: Feb  6 2017 17:11:52  CVS: $Id: mri_ca_normalize.c,v 1.69 2016/10/22 17:30:57 fischl Exp $  User: skoch  Machine: sup3hw.ad.nudz.cz  Platform: Linux  PlatformVersion: 3.16.0-4-amd64  CompilerName: GCC  CompilerVersion: 40400  
[MGH TAG 3]: mri_ca_normalize -c ctrl_pts.mgz -mask brainmask.mgz norm_template.mgz /hydra-db/hydra_io/vypocty/skoch/bin/freesurfer_dev_20170208/average/RB_all_2016-05-10.vc700.gca transforms/talairach.lta norm.mgz ProgramVersion: $Name:  $  TimeStamp: 2017/04/05-09:40:16-GMT  BuildTimeStamp: Feb  6 2017 17:11:52  CVS: $Id: mri_ca_normalize.c,v 1.69 2016/10/22 17:30:57 fischl Exp $  User: skoch  Machine: sup4.ad.nudz.cz  Platform: Linux  PlatformVersion: 3.16.0-4-amd64  CompilerName: GCC  CompilerVersion: 40400  
[MGH TAG 3]: mri_normalize -mprage -aseg aseg.presurf.mgz -mask brainmask.mgz norm.mgz brain.mgz ProgramVersion: $Name:  $  TimeStamp: 2017/04/05-13:18:28-GMT  BuildTimeStamp: Feb  6 2017 17:11:52  CVS: $Id: mri_normalize.c,v 1.92 2017/01/27 22:31:34 ah221 Exp $  User: skoch  Machine: sup4.ad.nudz.cz  Platform: Linux  PlatformVersion: 3.16.0-4-amd64  CompilerName: GCC  CompilerVersion: 40400  
[MGH TAG 3]: mri_segment -keep -mprage brain.mgz wm.seg.mgz 
[MGH TAG 3]: mri_edit_wm_with_aseg -keep-in wm.seg.mgz brain.mgz aseg.presurf.mgz wm.asegedit.mgz ProgramVersion: $Name:  $  TimeStamp: 2017/04/05-13:23:06-GMT  BuildTimeStamp: Feb  6 2017 17:11:52  CVS: $Id: mri_edit_wm_with_aseg.c,v 1.25 2012/03/29 13:17:38 fischl Exp $  User: skoch  Machine: sup4.ad.nudz.cz  Platform: Linux  PlatformVersion: 3.16.0-4-amd64  CompilerName: GCC  CompilerVersion: 40400  
[MGH TAG 3]: mri_pretess -keep wm.asegedit.mgz wm norm.mgz wm.mgz ProgramVersion: $Name:  $  TimeStamp: 2017/04/05-13:23:30-GMT  BuildTimeStamp: Feb  6 2017 17:11:52  CVS: $Id: mri_pretess.c,v 1.22 2013/08/30 18:12:25 mreuter Exp $  User: skoch  Machine: sup4.ad.nudz.cz  Platform: Linux  PlatformVersion: 3.16.0-4-amd64  CompilerName: GCC  CompilerVersion: 40400  
[MGH TAG 3]: mri_fill -a ../scripts/ponscc.cut.log -xform transforms/talairach.lta -segmentation aseg.auto_noCCseg.mgz wm.mgz filled.mgz ProgramVersion: $Name:  $  TimeStamp: 2017/04/05-13:23:34-GMT  BuildTimeStamp: Feb  6 2017 17:11:52  CVS: $Id: mri_fill.c,v 1.119 2011/10/25 14:09:58 fischl Exp $  User: skoch  Machine: sup4.ad.nudz.cz  Platform: Linux  PlatformVersion: 3.16.0-4-amd64  CompilerName: GCC  CompilerVersion: 40400```

@jdtournier
Copy link
Copy Markdown
Member Author

OK, I've added the version of the executable to the command history, so we can track that through also.

I'll merge this now, we can modify over the next few days if need be.

@jdtournier jdtournier merged commit 082def3 into tag_0.3.16 Apr 21, 2017
@jdtournier jdtournier deleted the store_command_history_in_header branch April 21, 2017 13:30
Lestropie added a commit that referenced this pull request May 10, 2017
Fixes for compiling against 3.0_RC1 branch.
Moved contents of MGH format "other" field from header comment entries to header key/value pairs.
Add contents of MGH_TAG_CMDLINE to header key/value entry "command_history" as in #962, and vice-versa.

std::string cmd = App::argv[0];
for (int n = 1; n < App::argc; ++n)
cmd += std::string(" \"") + App::argv[n] + "\"";
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdtournier Is it necessary to add double-quotes to every entry in argv? Could it not be reserved only for entries that contain whitespace?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is a bit ugly. But white spaces aren't the only potential problem. We'd have to watch for at least these characters: []*?%#~&$(){}\t\\"';:<> - i.e. absolutely anything that the shell might conceivably interpret. If the command we received contained these characters, then that's what the user intended, and they must have supplied the argument in such as way as to avoid interpretation by the shell. So we should respect that - the arguments the executable received have already been interpreted by the shell, so we should ensure that a straight copy/paste of the command works without the shell re-interpreting anything incorrectly.

Also, I think we should be using single quotes ' to avoid further variable expansion, etc. - otherwise an argument like stuff$n.mif would be interpreted when it should be left alone (although I completely agree that users who actually names their files like that deserve everything they get...).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reminds me of here, where I wrap paths in quotation marks only when necessary for shlex.split(). Admittedly that maybe needs to check for more than just whitespace; but that's what triggered the implementation. Why not simply use a similar heuristic here, i.e. only wrap a string / path in quotation marks if it contains special characters (which will be the vast minority of the case)?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor addition to this: Could probably use Path::basename(App::argv[0]). I'm not sure that keeping track of the location of the binary on the system for each command in the history is worth the bloat for the very rare instance where it might provide useful information.

@Lestropie
Copy link
Copy Markdown
Member

Another follow-up on this:

If a command takes multiple input images, and each contains its own command_history entry, what should the output header contain? One option would be to timestamp each entry, and sort them on combination. Though it starts to sound like it would require its own dedicated handling in the Header class rather than being just another key-value pair...

@jdtournier
Copy link
Copy Markdown
Member Author

If a command takes multiple input images, and each contains its own command_history entry, what should the output header contain? One option would be to timestamp each entry, and sort them on combination. Though it starts to sound like it would require its own dedicated handling in the Header class rather than being just another key-value pair...

This is an issue I'd raised a while back, and I'm not sure there's an obvious easy answer here... If we store a tree, it needs to be easily parsed by a human, I doubt we'll see these entries needing to be parsed by a computer any time soon (unless you guys have grand plans I'm not privy to). But you'd need to store a record of which input to the Nth command a particular history branch refers to, and ways of expressing this hierarchy. Maybe we need to prefix the command history entry with the filename of the file it refers to (as provided to the command storing the history), and some way of expressing the tree structure in a way that can easily be represented on a terminal screen. I'm not sure there's any worth in making the header structure any more complicated if it doesn't also make the display of this information any easier. From my point of view, if we can come up with a textual representation of the hierarchy, there is nothing stopping us from just storing this in the header using the current key: value format, and just dumping it out when needed, like we do the comments fields for instance. All it needs is for the formatting that carries the hierarchy information to be part of the text stored in these fields, for example:

$ dwi2mask dwi.mif mask.mif
$ dwi2tensor dwi.mif - | tensor2metric - -mask mask.mif -fa fa.mif

would need to store a structure like this:

[fa.mif] tensor2metric "-" "-mask" "mask.mif" "-fa" "fa.mif"  (version=3.0_RC1-186-g6034fc2f)
├── [-] dwi2tensor "dwi.mif" "-"  (version=3.0_RC1-186-g6034fc2f)
└── [mask.mif] dwi2mask "dwi.mif" "mask.mif"  (version=3.0_RC1-186-g6034fc2f)

There's no reason why we couldn't store that as-is in the header, simply prefixing each line with a command_history: key as we currently do. It might actually be relatively easy to manage appending to the history that way. For example, if the next command was:

$ mrthreshold fa.mif -top 300 sf.mif

Then the history would become:

[sf.mif] mrthreshold "fa.mif" "-top" "300" "sf.mif"   (version=3.0_RC1-186-g6034fc2f)
└── [fa.mif] tensor2metric "-" "-mask" "mask.mif" "-fa" "fa.mif"  (version=3.0_RC1-186-g6034fc2f)
    ├── [-] dwi2tensor "dwi.mif" "-"  (version=3.0_RC1-186-g6034fc2f)
    └── [mask.mif] dwi2mask "dwi.mif" "mask.mif"  (version=3.0_RC1-186-g6034fc2f)

which should be relatively easy to manage: the whole history for the arguments gets indented by 4 or so spaces, the leading line prefixed by └── , and that whole chunk gets added to the history for each of the image arguments (ideally including arguments to options). So I think we could deal with this.

A bigger issue though is how to handle this transparently. At the moment, the history update is handled by the Header::create() call, based on the information already present in the incoming template Header. It works since the incoming template Header is itself typically read from (one of) the input image. But if we want to inject the history of all the inputs to the current command, we need access to the contents of all of the Headers at the point where any output image gets created. This doesn't match the way this is handled currently. The most obvious way to handle this seamlessly would be to have the command-line parsing backend load all image headers upfront before run() is invoked. This wouldn't be impossible to do, but there will be exceptions to this in cases where we can't determine whether a particular argument is going to be an image or something else (mrcalc immediately springs to mind). Relying on each command explicitly updating this field is not a robust solution. Maybe we could have a system whereby it is expected that all inputs have been opened before the first output is created, ensuring that the information has been gathered by that point - this may be the least intrusive way of doing this... assuming we want to go along with the whole idea, of course...

@jdtournier
Copy link
Copy Markdown
Member Author

By the way, I do think it would be worth removing the quotes when they're not required. We just need a semi-robust way of detecting characters that would require quoting (whitespaces probably aren't enough).

Also, I'd rather keep the full name of the argv[0] if possible. 99% of the time, this will only be the actual command basename anyway, since that's what the user typed. In those rare cases where it's not, it might actually prove important to know that the command wasn't in the default path... But admittedly the git history SHA1 is probably more important here.

@thijsdhollander
Copy link
Copy Markdown
Contributor

I'm not entirely convinced the benefits are worth the hassle, and the level of intrusion in commands (and potential performance). As @dchristiaens brought up in that original discussion, I too (any others that I know) use non-MRtrix steps at times. And then there's manual steps (e.g. ROI creation or editing) in many typical scenarios too...

For me, if any benefit at all, there's mostly some value into storing the last command an image was generated from, as some sort of automated note-taking functionality. The current implementation already goes beyond that by keeping the (linear) history, but even there it creates such a big bulk of almost mess when outputting that, that I've actually never used it for any purpose. I'd rather look at the 'mrinfo' of the command/image before and look at what produced that, if I'd ever need to.

Thinking about that though, this may be something: if an image just stores the last command that generated it (which has the source images file names in there too), and applies a convention as to what path these are relative to (or stores this (relative) path), then you could have an mrshowhistory command that goes and backtracks as far as it can in existing headers, and shows you this kind of history tree output. It's of course not robust to files having moved, but then again if actual files have moved, one could question the value of a history (that will also have path issues) for the purposes of re-creating stuff as well. As @jdtournier comments, I suppose for the foreseeable future the value is mostly in just showing this history to the user?

@Lestropie
Copy link
Copy Markdown
Member

But if we want to inject the history of all the inputs to the current command, we need access to the contents of all of the Headers at the point where any output image gets created.

This doesn't just apply to command-history, but more generally to any header key-value entry in the event of a command that takes multiple input files. I've encountered this a couple of times recently (e.g. this commit); I've also dealt with a similar issue in tckedit. There should perhaps be a helper function somewhere for generating the output header key/value set based on input headers? That would at least reduce this risk:

Relying on each command explicitly updating this field is not a robust solution.

More generally, I think I'm in the camp of only storing the generating command, not a full command history. With the potential for non-MRtrix3 commands & use of non-MRtrix3 image formats in intermediate steps, potential non-linear paths, and no immediate need to be able to re-produce processing steps in an automated fashion using this functionality alone, it might be a rabbit hole best avoided. This would also be much more compatible with the Python scripts, since the "generating command" stored in the final output image header(s) could be over-ridden by the script library to represent the user's call to the script, rather than the internal mrconvert call that happens to be the last stage of the script.

@thijsdhollander
Copy link
Copy Markdown
Contributor

More generally, I think I'm in the camp of only storing the generating command, not a full command history.

Yes, agreed. That's probably the best (easiest) defined behaviour as well. As I mentioned above, this still doesn't limit users from recreating a pretty detailed history in a well-controlled environment.

@jdtournier
Copy link
Copy Markdown
Member Author

More generally, I think I'm in the camp of only storing the generating command, not a full command history.

Yes, agreed. That's probably the best (easiest) defined behaviour as well. As I mentioned above, this still doesn't limit users from recreating a pretty detailed history in a well-controlled environment.

Just thought I'd point out that this won't handle piped commands... There'd be no permanent record of how the temporary files were generated.

But generally, I don't think we necessarily need to vastly improve on what we already have. It's a nice bonus, and if someone wants to improve it, they'd be more than welcome. But this has to remain a very low priority issue...

This doesn't just apply to command-history, but more generally to any header key-value entry in the event of a command that takes multiple input files. I've encountered this a couple of times recently (e.g. this commit); I've also dealt with a similar issue in tckedit. There should perhaps be a helper function somewhere for generating the output header key/value set based on input headers?

As far as I can tell, the correct action here is by necessity command-specific, so I don't think we can generalise enormously. Helper functions may help for common cases, like the one you highlighted in mrcalc - i.e. when multiple inputs are equivalent, you might elect to preserve the DW scheme or PE scheme if they match, and remove them otherwise. But you probably don't want to do that if one of the images is e.g. a mask. So for instance, as far as I can tell, your change to mrcalc would remove the DW scheme from a set of DW images if we did something innocuous-looking like:

$ mrcalc dwi.mif mask.mif -mult dwi_masked.mif

But then it's almost impossible to guarantee correct behaviour in general, since simply swapping the inputs would result in no DW scheme anyway if it wasn't already there in the mask.mif image... Personally I reckon I'd stick with the previous behaviour (I hadn't realised you'd made that change), and maybe document that most of the header information is gathered from the first image on the command-line - there's too much scope for unexpected problems with the change you made, I think...

@Lestropie
Copy link
Copy Markdown
Member

Just thought I'd point out that this won't handle piped commands... There'd be no permanent record of how the temporary files were generated.

Not trivially anyway. Technically the temporary file's header entry should contain within it the segment of the piped command used to generate that temporary file, so theoretically it might be possible to correspondingly stitch the full piped command back together; just need to differentiate between input & output pipes. Even with non-linear pipe arrangements, you could detect the fact that the command-line argument is a temporary file rather than a --' and substitute accordingly.

So for instance, as far as I can tell, your change to mrcalc would remove the DW scheme from a set of DW images ...

Nope: only erases if both images contain a DW scheme, and they don't match. A mask image shouldn't contain a DW scheme (it should be in prior_dw_scheme).

It does however mean that the DW scheme will be retained if the DWI is the first argument, whereas it'll be absent if the mask is the first argument. Hence the suggestion for more "advanced" handling of header "consistency / variance", that may be sufficiently complex to justify embedding in helper functions.

@jdtournier
Copy link
Copy Markdown
Member Author

Technically the temporary file's header entry should contain within it the segment of the piped command used to generate that temporary file, so theoretically it might be possible to correspondingly stitch the full piped command back together; just need to differentiate between input & output pipes. Even with non-linear pipe arrangements, you could detect the fact that the command-line argument is a temporary file rather than a --' and substitute accordingly.

Yes, that might be possible. Just to make sure I understand this right, the idea would be to store the full multi-stage pipeline command in the header as a single command_history entry, so that for a set of commands like my previous example:

$ dwi2mask dwi.mif mask.mif
$ dwi2tensor dwi.mif - | tensor2metric - -mask mask.mif -fa fa.mif

then mask.mif would have a single entry with

dwi2mask dwi.mif mask.mif`  (version=3.0_RC1-186-g6034fc2f)

while fa.mif would have a more complex entry, ideally:

dwi2tensor dwi.mif - | tensor2metric - -mask mask.mif -fa fa.mif  (version=3.0_RC1-186-g6034fc2f)

And this would be achieved by reading the history from the temporary file produced by dwi2tensor, detecting it as a temporary input, and prepending the corresponding entry to the command_history with a trailing |...?

So that would do-able, with a few gotchas. As you mention, handling multiple temporary inputs to the same command is going to be tricky (but then it's a very rarely used feature - might be worth disabling altogether...?). Other minor issue is what to do if the different commands happen to have different version strings - maybe store them explicitly in order at the end, e.g. like this?

dwi2tensor dwi.mif - | tensor2metric - -mask mask.mif -fa fa.mif  (version=3.0_RC1-185-g2b38f9a 3.0_RC1-186-g6034fc2f)

And ultimately, this still suffers from the problem I raised before: if we want this to happen transparently, we need to gather the information about all the input headers before creating the output header. I reckon it would be sufficient to ensure all inputs are read before any output is created - maybe enforced through runtime checks just to make sure...

@jdtournier
Copy link
Copy Markdown
Member Author

Nope: only erases if both images contain a DW scheme, and they don't match. A mask image shouldn't contain a DW scheme (it should be in prior_dw_scheme).

Neat. Hadn't picked up on that. All good.

It does however mean that the DW scheme will be retained if the DWI is the first argument, whereas it'll be absent if the mask is the first argument. Hence the suggestion for more "advanced" handling of header "consistency / variance", that may be sufficiently complex to justify embedding in helper functions.

Yes, I can see what you mean. I agree that would be useful, particularly for multi-input single-output apps.

@Lestropie
Copy link
Copy Markdown
Member

... the idea would be to store the full multi-stage pipeline command in the header as a single command_history entry ...

Well, would probably remove the "_history" bit if it's only one entry :-P

And this would be achieved by reading the history from the temporary file produced by dwi2tensor, detecting it as a temporary input, and prepending the corresponding entry to the command_history with a trailing |...?

👍

As you mention, handling multiple temporary inputs to the same command is going to be tricky (but then it's a very rarely used feature - might be worth disabling altogether...?).

Actually I don't think so. Let's use the example from the documentation:

$ dwi2tensor /data/DICOM/ - | tensor2metric - -mask $(dwi2mask /data/DICOM/ - | maskfilter - erode -npass 3 - ) -vec ev.mif -fa - | mrthreshold - -top 300 highFA.mif

The temporary image created by dwi2tensor contains:
dwi2tensor /data/DICOM/ -

There's then a second temporary image created by the combination of tensor2metric & maskfilter, which will contain:
dwi2mask /data/DICOM/ - | maskfilter - erode -npass 3 -

For tensor2metric, argv should then contain:
tensor2metric - -mask mrtrix-tmp-######.mif -vec ev.mif -fa -

The shell is responsible for taking the standard output of the call encapsulated within $() and placing it into the argv list. In the case of piped images, that's a temporary image name string. Therefore, the hypothetical code responsible for construction of the command string would be able to tell the difference between an input piped image "-" (in which case the command string should be prepended with that of the piped image, followed by a "|"), and an input non-piped temporary image (in which case the name of that file within the command string should be replaced with the command string of the temporary image, surrounded by "$()"). Differentiating between input piped images and output piped images can probably be done using relative position alone.

Other minor issue is what to do if the different commands happen to have different version strings ...

In tckedit I write the value "variable" whenever the value of an entry is different between different input files. Realistically, if you're trying to debug an issue, and you see different MRtrix3 code versions within the single-line command string, you wouldn't be debugging based on the different versions of different commands - you'll be telling the user they have a version mismatch, and to check their installation and then re-generate the result.

@jdtournier
Copy link
Copy Markdown
Member Author

As you mention, handling multiple temporary inputs to the same command is going to be tricky (but then it's a very rarely used feature - might be worth disabling altogether...?).

Actually I don't think so. Let's use the example from the documentation:

Yes, I don't mean it's impossible, just messy. And given that I'm probably the only person to have ever used the feature, I think we could just decide to simplify and limit passing of temporary files to pipes only, which would then make this a lot simpler to manage...

Unless this happens to be something you guys use all the time...?

Differentiating between input piped images and output piped images can probably be done using relative position alone.

That's not an issue - the code needs to know this to figure out whether to push the image name out to stdout or delete the file (see how this is done in the ImageIO::Pipe handler)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants