multiSSH3 6.34__tar.gz → 6.35__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. {multissh3-6.34/multiSSH3.egg-info → multissh3-6.35}/PKG-INFO +7 -5
  2. {multissh3-6.34 → multissh3-6.35}/README.md +6 -4
  3. {multissh3-6.34 → multissh3-6.35}/mssh_complete.py +39 -4
  4. {multissh3-6.34 → multissh3-6.35/multiSSH3.egg-info}/PKG-INFO +7 -5
  5. {multissh3-6.34 → multissh3-6.35}/multiSSH3.egg-info/SOURCES.txt +3 -0
  6. {multissh3-6.34 → multissh3-6.35}/multiSSH3.py +418 -116
  7. {multissh3-6.34 → multissh3-6.35}/tests/conftest.py +19 -0
  8. {multissh3-6.34 → multissh3-6.35}/tests/test_cli_option_words.py +102 -0
  9. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_context.py +70 -3
  10. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_diagnose.py +20 -0
  11. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_picker.py +4 -2
  12. {multissh3-6.34 → multissh3-6.35}/tests/test_curses_event_loop.py +228 -0
  13. {multissh3-6.34 → multissh3-6.35}/tests/test_options_after_command.py +14 -0
  14. {multissh3-6.34 → multissh3-6.35}/tests/test_runtime_branch_matrix.py +1 -1
  15. multissh3-6.35/tests/test_shared_lines.py +219 -0
  16. {multissh3-6.34 → multissh3-6.35}/tests/test_stream_reader.py +7 -5
  17. multissh3-6.35/tests/test_streamed_output.py +228 -0
  18. multissh3-6.35/tests/test_waiting.py +244 -0
  19. {multissh3-6.34 → multissh3-6.35}/LICENSE +0 -0
  20. {multissh3-6.34 → multissh3-6.35}/MANIFEST.in +0 -0
  21. {multissh3-6.34 → multissh3-6.35}/multiSSH3.egg-info/dependency_links.txt +0 -0
  22. {multissh3-6.34 → multissh3-6.35}/multiSSH3.egg-info/entry_points.txt +0 -0
  23. {multissh3-6.34 → multissh3-6.35}/multiSSH3.egg-info/top_level.txt +0 -0
  24. {multissh3-6.34 → multissh3-6.35}/pytest.ini +0 -0
  25. {multissh3-6.34 → multissh3-6.35}/setup.cfg +0 -0
  26. {multissh3-6.34 → multissh3-6.35}/setup.py +0 -0
  27. {multissh3-6.34 → multissh3-6.35}/tests/bench_complete.py +0 -0
  28. {multissh3-6.34 → multissh3-6.35}/tests/complete_shell_harness.py +0 -0
  29. {multissh3-6.34 → multissh3-6.35}/tests/data/run_command_golden.json +0 -0
  30. {multissh3-6.34 → multissh3-6.35}/tests/shells/Dockerfile.bash5.1 +0 -0
  31. {multissh3-6.34 → multissh3-6.35}/tests/shells/Dockerfile.bash5.3 +0 -0
  32. {multissh3-6.34 → multissh3-6.35}/tests/shells/Dockerfile.centos7 +0 -0
  33. {multissh3-6.34 → multissh3-6.35}/tests/shells/Dockerfile.current +0 -0
  34. {multissh3-6.34 → multissh3-6.35}/tests/shells/Dockerfile.legacy +0 -0
  35. {multissh3-6.34 → multissh3-6.35}/tests/shells/Dockerfile.modern +0 -0
  36. {multissh3-6.34 → multissh3-6.35}/tests/shells/run_matrix.sh +0 -0
  37. {multissh3-6.34 → multissh3-6.35}/tests/test_cli_branches.py +0 -0
  38. {multissh3-6.34 → multissh3-6.35}/tests/test_cli_processing.py +0 -0
  39. {multissh3-6.34 → multissh3-6.35}/tests/test_closed_output.py +0 -0
  40. {multissh3-6.34 → multissh3-6.35}/tests/test_color_helpers.py +0 -0
  41. {multissh3-6.34 → multissh3-6.35}/tests/test_command_terminal.py +0 -0
  42. {multissh3-6.34 → multissh3-6.35}/tests/test_compact_hostnames.py +0 -0
  43. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_blesh.py +0 -0
  44. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_cli.py +0 -0
  45. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_editor.py +0 -0
  46. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_engine.py +0 -0
  47. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_eval.py +0 -0
  48. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_hosts.py +0 -0
  49. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_hoststate.py +0 -0
  50. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_index.py +0 -0
  51. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_keys.py +0 -0
  52. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_lex.py +0 -0
  53. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_mssh_helpers.py +0 -0
  54. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_password_spellings.py +0 -0
  55. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_probe.py +0 -0
  56. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_shells.py +0 -0
  57. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_start.py +0 -0
  58. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_store.py +0 -0
  59. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_suggest.py +0 -0
  60. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_terminal.py +0 -0
  61. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_untrusted.py +0 -0
  62. {multissh3-6.34 → multissh3-6.35}/tests/test_complete_zsh_options.py +0 -0
  63. {multissh3-6.34 → multissh3-6.35}/tests/test_config_cli.py +0 -0
  64. {multissh3-6.34 → multissh3-6.35}/tests/test_config_validate.py +0 -0
  65. {multissh3-6.34 → multissh3-6.35}/tests/test_config_values.py +0 -0
  66. {multissh3-6.34 → multissh3-6.35}/tests/test_control_master_config.py +0 -0
  67. {multissh3-6.34 → multissh3-6.35}/tests/test_copy_id.py +0 -0
  68. {multissh3-6.34 → multissh3-6.35}/tests/test_curses_branches.py +0 -0
  69. {multissh3-6.34 → multissh3-6.35}/tests/test_curses_tui.py +0 -0
  70. {multissh3-6.34 → multissh3-6.35}/tests/test_curses_wide_chars.py +0 -0
  71. {multissh3-6.34 → multissh3-6.35}/tests/test_expand_hostnames.py +0 -0
  72. {multissh3-6.34 → multissh3-6.35}/tests/test_file_trust.py +0 -0
  73. {multissh3-6.34 → multissh3-6.35}/tests/test_harness_smoke.py +0 -0
  74. {multissh3-6.34 → multissh3-6.35}/tests/test_helpers_extra.py +0 -0
  75. {multissh3-6.34 → multissh3-6.35}/tests/test_history_replay.py +0 -0
  76. {multissh3-6.34 → multissh3-6.35}/tests/test_host.py +0 -0
  77. {multissh3-6.34 → multissh3-6.35}/tests/test_host_validity.py +0 -0
  78. {multissh3-6.34 → multissh3-6.35}/tests/test_hosts_file.py +0 -0
  79. {multissh3-6.34 → multissh3-6.35}/tests/test_identities_only.py +0 -0
  80. {multissh3-6.34 → multissh3-6.35}/tests/test_ipmi_lockout_wait.py +0 -0
  81. {multissh3-6.34 → multissh3-6.35}/tests/test_ipmi_logins.py +0 -0
  82. {multissh3-6.34 → multissh3-6.35}/tests/test_key_path_home.py +0 -0
  83. {multissh3-6.34 → multissh3-6.35}/tests/test_login_drop_retry.py +0 -0
  84. {multissh3-6.34 → multissh3-6.35}/tests/test_main_lifecycle.py +0 -0
  85. {multissh3-6.34 → multissh3-6.35}/tests/test_narrow_terminal.py +0 -0
  86. {multissh3-6.34 → multissh3-6.35}/tests/test_no_hosts.py +0 -0
  87. {multissh3-6.34 → multissh3-6.35}/tests/test_option_no_effect.py +0 -0
  88. {multissh3-6.34 → multissh3-6.35}/tests/test_output_merge.py +0 -0
  89. {multissh3-6.34 → multissh3-6.35}/tests/test_output_robustness.py +0 -0
  90. {multissh3-6.34 → multissh3-6.35}/tests/test_packaging.py +0 -0
  91. {multissh3-6.34 → multissh3-6.35}/tests/test_password_handling.py +0 -0
  92. {multissh3-6.34 → multissh3-6.35}/tests/test_passwords_out_of_ps.py +0 -0
  93. {multissh3-6.34 → multissh3-6.35}/tests/test_run_command.py +0 -0
  94. {multissh3-6.34 → multissh3-6.35}/tests/test_run_command_branches.py +0 -0
  95. {multissh3-6.34 → multissh3-6.35}/tests/test_run_command_characterization.py +0 -0
  96. {multissh3-6.34 → multissh3-6.35}/tests/test_run_command_exit.py +0 -0
  97. {multissh3-6.34 → multissh3-6.35}/tests/test_run_on_hosts.py +0 -0
  98. {multissh3-6.34 → multissh3-6.35}/tests/test_run_on_hosts_modes.py +0 -0
  99. {multissh3-6.34 → multissh3-6.35}/tests/test_runtime_dir.py +0 -0
  100. {multissh3-6.34 → multissh3-6.35}/tests/test_smoke_optional.py +0 -0
  101. {multissh3-6.34 → multissh3-6.35}/tests/test_ssh_error_text.py +0 -0
  102. {multissh3-6.34 → multissh3-6.35}/tests/test_ssh_timeouts.py +0 -0
  103. {multissh3-6.34 → multissh3-6.35}/tests/test_sshpass_exit.py +0 -0
  104. {multissh3-6.34 → multissh3-6.35}/tests/test_start_run_on_hosts.py +0 -0
  105. {multissh3-6.34 → multissh3-6.35}/tests/test_stopped_commands.py +0 -0
  106. {multissh3-6.34 → multissh3-6.35}/tests/test_tui_ssh_notices.py +0 -0
  107. {multissh3-6.34 → multissh3-6.35}/tests/test_unavailable_hosts.py +0 -0
  108. {multissh3-6.34 → multissh3-6.35}/tests/test_underscore_settings.py +0 -0
  109. {multissh3-6.34 → multissh3-6.35}/tests/test_wide_wrap.py +0 -0
  110. {multissh3-6.34 → multissh3-6.35}/tests/vt_grid.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multiSSH3
3
- Version: 6.34
3
+ Version: 6.35
4
4
  Summary: Run commands on multiple hosts via SSH
5
5
  Home-page: https://github.com/yufei-pan/multiSSH3
6
6
  Author: Yufei Pan
@@ -603,10 +603,12 @@ mssh -q -- web1 grep -r TODO /srv
603
603
 
604
604
  ### `-ea, --extraargs EXTRAARGS` | _`DEFAULT_EXTRA_ARGS`_
605
605
  - Extra arguments to pass to the ssh / rsync / scp command. Put in one string for multiple arguments.
606
+ - The word after `-ea` is its value, also when it starts with `-`: `-ea '-p 2222'`, `-ea -A`. Quote an option together with its value: with `-ea -p 2222` the quotes were forgotten, and mssh stops, as it does for a password option there. ( mssh 6.34 and older need `-ea='-p 2222'`, which still works. )
606
607
  - The string is split into arguments as a shell would: quote an argument that contains spaces.
607
608
  - For a file copy with rsync, ssh's options that take a value go to the ssh that rsync runs, and the other arguments to rsync: the option followed by its value as the next word (`-o ConnectTimeout=5`, `-p 2222`, `-i FILE`, `-J HOST`, `-F FILE`, `-l USER`, ...), or `-oName=value` and `-pPORT`. scp takes ssh's options itself; ssh's `-p PORT` is given to it as `-P PORT`.
608
609
  - Example:
609
610
  ```bash
611
+ mssh -ea '-p 2222' allServers uptime
610
612
  mssh -ea="--delete" -f ./data/ allServers /tmp/data/
611
613
  mssh -ea="-o ProxyCommand='ssh -W %h:%p bastion'" allServers uptime
612
614
  ```
@@ -717,7 +719,7 @@ mssh -f ./data/ allServers /tmp/data/
717
719
 
718
720
  ### `-ia, --ipmi_args IPMI_ARGS` | _`DEFAULT_IPMI_ARGS`_
719
721
  - Extra arguments for ipmitool (or redfishtool) in `--ipmi` mode, in one string, placed as they are before the command. `-ea` arguments only go to ssh / scp / rsync.
720
- - Ex: `mssh -M -ia='-I lanplus' bmc[1-4] 'power status'` runs `ipmitool -H <address> -U <user> -E -I lanplus power status`, with the password in its environment. Use `=`, as the value starts with `-`.
722
+ - Ex: `mssh -M -ia '-I lanplus' bmc[1-4] 'power status'` runs `ipmitool -H <address> -U <user> -E -I lanplus power status`, with the password in its environment. The word after `-ia` is its value, also when it starts with `-`; quote an option together with its value ( mssh 6.34 and older need `-ia='-I lanplus'` ).
721
723
 
722
724
  ### `-im, --ipmi_method IPMI_METHOD` | _`DEFAULT_IPMI_METHOD`_
723
725
  - How `--ipmi` reaches a host:
@@ -972,7 +974,7 @@ Option names count in full only: `--user` is not `--username` and `-c` is not `-
972
974
 
973
975
  Putting the command after `--` is no substitute for quotes: every word after the hosts is a command of its own, so `mssh web1 -- bash -c 'echo hi'` runs `bash`, `-c` and `echo hi` one after another (when each word is a single word, as in `mssh web1 -- ls -la`, mssh asks whether to join them).
974
976
 
975
- Below is a sample help message output from multiSSH3 6.34 @ 2026-10-16 (no config file, 120 columns; the lines after the options list the programs found on that machine)
977
+ Below is a sample help message output from multiSSH3 6.35 @ 2026-10-16 (no config file, 120 columns; the lines after the options list the programs found on that machine)
976
978
 
977
979
  ```bash
978
980
  $ mssh -h
@@ -1003,7 +1005,7 @@ options:
1003
1005
  The identity file to use to connect to the hosts. Implies --use_key. Specify a folder for program to search for a key. Use option without value to use ~/.ssh/ (default: None)
1004
1006
  -uk, --use_key Attempt to use public key file to connect to the hosts. (default: False)
1005
1007
  -ea, --extraargs EXTRAARGS
1006
- Extra arguments to pass to the ssh / rsync / scp command. (Use -ia for ipmi arguments) Put in one string for multiple arguments.Use "=" ! Ex. -ea="--delete" (default: None)
1008
+ Extra arguments to pass to the ssh / rsync / scp command. (Use -ia for ipmi arguments) Put in one string for multiple arguments. The word after -ea is its value, also when it starts with "-": quote an option with its value. Ex. -ea "-p 2222 -A" (default: None)
1007
1009
  -11, --oneonone Run one corresponding command on each host. (default: False)
1008
1010
  -f, --file FILE The file to be copied to the hosts. Use -f multiple times to copy multiple files
1009
1011
  -s, -fs, --file_sync [FILE_SYNC]
@@ -1028,7 +1030,7 @@ options:
1028
1030
  -ip, --ipmi_password IPMI_PASSWORD
1029
1031
  The password to use to connect to the hosts via ipmi. (default: set, not shown)
1030
1032
  -ia, --ipmi_args IPMI_ARGS
1031
- The arguments passed to ipmitool to use to connect to the hosts via ipmi. (default: )
1033
+ The arguments passed to ipmitool to use to connect to the hosts via ipmi. The word after -ia is its value, also when it starts with "-": quote an option with its value. Ex. -ia "-I lanplus" (default: )
1032
1034
  -im, --ipmi_method IPMI_METHOD
1033
1035
  The method to use to connect to the hosts via ipmi. (default: ipmitool)
1034
1036
  -irlt, --ipmi_retry_lockout_time IPMI_RETRY_LOCKOUT_TIME
@@ -577,10 +577,12 @@ mssh -q -- web1 grep -r TODO /srv
577
577
 
578
578
  ### `-ea, --extraargs EXTRAARGS` | _`DEFAULT_EXTRA_ARGS`_
579
579
  - Extra arguments to pass to the ssh / rsync / scp command. Put in one string for multiple arguments.
580
+ - The word after `-ea` is its value, also when it starts with `-`: `-ea '-p 2222'`, `-ea -A`. Quote an option together with its value: with `-ea -p 2222` the quotes were forgotten, and mssh stops, as it does for a password option there. ( mssh 6.34 and older need `-ea='-p 2222'`, which still works. )
580
581
  - The string is split into arguments as a shell would: quote an argument that contains spaces.
581
582
  - For a file copy with rsync, ssh's options that take a value go to the ssh that rsync runs, and the other arguments to rsync: the option followed by its value as the next word (`-o ConnectTimeout=5`, `-p 2222`, `-i FILE`, `-J HOST`, `-F FILE`, `-l USER`, ...), or `-oName=value` and `-pPORT`. scp takes ssh's options itself; ssh's `-p PORT` is given to it as `-P PORT`.
582
583
  - Example:
583
584
  ```bash
585
+ mssh -ea '-p 2222' allServers uptime
584
586
  mssh -ea="--delete" -f ./data/ allServers /tmp/data/
585
587
  mssh -ea="-o ProxyCommand='ssh -W %h:%p bastion'" allServers uptime
586
588
  ```
@@ -691,7 +693,7 @@ mssh -f ./data/ allServers /tmp/data/
691
693
 
692
694
  ### `-ia, --ipmi_args IPMI_ARGS` | _`DEFAULT_IPMI_ARGS`_
693
695
  - Extra arguments for ipmitool (or redfishtool) in `--ipmi` mode, in one string, placed as they are before the command. `-ea` arguments only go to ssh / scp / rsync.
694
- - Ex: `mssh -M -ia='-I lanplus' bmc[1-4] 'power status'` runs `ipmitool -H <address> -U <user> -E -I lanplus power status`, with the password in its environment. Use `=`, as the value starts with `-`.
696
+ - Ex: `mssh -M -ia '-I lanplus' bmc[1-4] 'power status'` runs `ipmitool -H <address> -U <user> -E -I lanplus power status`, with the password in its environment. The word after `-ia` is its value, also when it starts with `-`; quote an option together with its value ( mssh 6.34 and older need `-ia='-I lanplus'` ).
695
697
 
696
698
  ### `-im, --ipmi_method IPMI_METHOD` | _`DEFAULT_IPMI_METHOD`_
697
699
  - How `--ipmi` reaches a host:
@@ -946,7 +948,7 @@ Option names count in full only: `--user` is not `--username` and `-c` is not `-
946
948
 
947
949
  Putting the command after `--` is no substitute for quotes: every word after the hosts is a command of its own, so `mssh web1 -- bash -c 'echo hi'` runs `bash`, `-c` and `echo hi` one after another (when each word is a single word, as in `mssh web1 -- ls -la`, mssh asks whether to join them).
948
950
 
949
- Below is a sample help message output from multiSSH3 6.34 @ 2026-10-16 (no config file, 120 columns; the lines after the options list the programs found on that machine)
951
+ Below is a sample help message output from multiSSH3 6.35 @ 2026-10-16 (no config file, 120 columns; the lines after the options list the programs found on that machine)
950
952
 
951
953
  ```bash
952
954
  $ mssh -h
@@ -977,7 +979,7 @@ options:
977
979
  The identity file to use to connect to the hosts. Implies --use_key. Specify a folder for program to search for a key. Use option without value to use ~/.ssh/ (default: None)
978
980
  -uk, --use_key Attempt to use public key file to connect to the hosts. (default: False)
979
981
  -ea, --extraargs EXTRAARGS
980
- Extra arguments to pass to the ssh / rsync / scp command. (Use -ia for ipmi arguments) Put in one string for multiple arguments.Use "=" ! Ex. -ea="--delete" (default: None)
982
+ Extra arguments to pass to the ssh / rsync / scp command. (Use -ia for ipmi arguments) Put in one string for multiple arguments. The word after -ea is its value, also when it starts with "-": quote an option with its value. Ex. -ea "-p 2222 -A" (default: None)
981
983
  -11, --oneonone Run one corresponding command on each host. (default: False)
982
984
  -f, --file FILE The file to be copied to the hosts. Use -f multiple times to copy multiple files
983
985
  -s, -fs, --file_sync [FILE_SYNC]
@@ -1002,7 +1004,7 @@ options:
1002
1004
  -ip, --ipmi_password IPMI_PASSWORD
1003
1005
  The password to use to connect to the hosts via ipmi. (default: set, not shown)
1004
1006
  -ia, --ipmi_args IPMI_ARGS
1005
- The arguments passed to ipmitool to use to connect to the hosts via ipmi. (default: )
1007
+ The arguments passed to ipmitool to use to connect to the hosts via ipmi. The word after -ia is its value, also when it starts with "-": quote an option with its value. Ex. -ia "-I lanplus" (default: )
1006
1008
  -im, --ipmi_method IPMI_METHOD
1007
1009
  The method to use to connect to the hosts via ipmi. (default: ipmitool)
1008
1010
  -irlt, --ipmi_retry_lockout_time IPMI_RETRY_LOCKOUT_TIME
@@ -16,7 +16,7 @@ import sys
16
16
  import time
17
17
  from bisect import bisect_left
18
18
 
19
- version = '0.7'
19
+ version = '0.8'
20
20
  __version__ = version
21
21
  PROTOCOL = 'MSSHC1'
22
22
  MSSH_NAMES = ('mssh', 'mssh3', 'multissh', 'multissh3', 'multiSSH3')
@@ -166,6 +166,31 @@ def split_at_cursor(before, after):
166
166
 
167
167
  #%% ------------ Option spec ----------------
168
168
  PASSWORD_DESTS = frozenset(('password', 'ipmi_password'))
169
+ # options passing options on to another program (-ea, -ia): mssh 6.35+ takes the word after them as
170
+ # their value even when it starts with '-' (its _with_passed_on_values), except as takes_passed_on_value says
171
+ PASSED_ON_DESTS = frozenset(('extraargs', 'ipmi_args'))
172
+ # the letters of the options needing a value of their own, by where they pass them: ssh, ipmitool
173
+ PASSED_ON_BARE_VALUE_LETTERS = {
174
+ 'extraargs': frozenset('BbcDEeFIiJLlmOoPpQRSWw'),
175
+ 'ipmi_args': frozenset('AbBCdefHIklLmNoOpPRtTUyz'),
176
+ }
177
+
178
+ def takes_passed_on_value(dest, text, spec):
179
+ '''
180
+ Whether -ea or -ia (dest) takes text, the word after it, as its value, as mssh's _takes_passed_on_value
181
+ reads it: not a bare option needing a value of its own (-ea -p 2222: the quotes were forgotten), nor a
182
+ word giving mssh's password; mssh stops at those.
183
+ '''
184
+ if not text.startswith('-') or ' ' in text:
185
+ return True
186
+ if len(text) == 2 and text[1] in PASSED_ON_BARE_VALUE_LETTERS[dest]:
187
+ return False
188
+ if text.startswith('-ip'):
189
+ return False
190
+ if text[:2] == '-p' and text[2:].isdigit():
191
+ return True
192
+ option = spec.lookup(text)[0]
193
+ return option is None or option.dest not in PASSWORD_DESTS
169
194
  # options naming files mssh reads its hosts and env aliases from, at Enter
170
195
  LOOKUP_DESTS = frozenset(('env_file', 'env_files', 'host_file', 'config_file'))
171
196
  PATH_DESTS = frozenset(('file', 'file_sync', 'env_file', 'env_files', 'config_file', 'store_config_file', 'host_file', 'history_file'))
@@ -506,9 +531,12 @@ def assign_roles(words, spec, current=None):
506
531
  pending = None
507
532
  # the word after a password option is its password, whatever it looks like: argparse stops
508
533
  # at "-p -secret", but the shell's history keeps the line, and a password is never another word.
509
- # argparse takes a dash word as the value when it holds a space and names no option
510
- # (-ea "-o StrictHostKeyChecking=no"); mssh's -11 makes it read negative numbers as options
511
- if not dashed or option.dest in PASSWORD_DESTS or (' ' in text and not spec.names_option(text)):
534
+ # mssh takes the word after -ea and -ia as their value (PASSED_ON_DESTS). argparse takes a dash
535
+ # word as the value when it holds a space and names no option (-u "-o x"); mssh's -11 makes it
536
+ # read negative numbers as options
537
+ # (a word being typed in an open quote will hold more: "-I lanplus")
538
+ passed_on = option.dest in PASSED_ON_DESTS and (takes_passed_on_value(option.dest, text, spec) or (is_current and token.open_quote))
539
+ if not dashed or option.dest in PASSWORD_DESTS or passed_on or (' ' in text and not spec.names_option(text)):
512
540
  roles.append((ROLE_VALUE, option))
513
541
  continue
514
542
  if text == '--' and not is_current and not after_separator:
@@ -3616,10 +3644,16 @@ class Engine(object):
3616
3644
  have_hosts = False
3617
3645
  run = None # where the unquoted remote command starts in argv
3618
3646
  pending = None # nargs of an option still taking the words that follow as values
3647
+ passed_on = None # -ea's or -ia's dest: the word may be its value even when it starts with '-'
3619
3648
  paths = False
3620
3649
  read = []
3621
3650
  stop = len(argv)
3622
3651
  for index, token in enumerate(argv):
3652
+ if passed_on:
3653
+ dest, passed_on = passed_on, None
3654
+ if takes_passed_on_value(dest, token.text, self.spec):
3655
+ pending = None
3656
+ continue
3623
3657
  if token.text == '--':
3624
3658
  stop = index
3625
3659
  break
@@ -3642,6 +3676,7 @@ class Engine(object):
3642
3676
  option, attached, _ = readings[-1]
3643
3677
  if attached is None and option.nargs != '0':
3644
3678
  pending = option.nargs
3679
+ passed_on = option.dest if option.dest in PASSED_ON_DESTS else None
3645
3680
  if run is None and any(option.dest in ('file', 'file_sync', 'gather_mode') for option, _, _ in readings):
3646
3681
  paths = True
3647
3682
  if run is not None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multiSSH3
3
- Version: 6.34
3
+ Version: 6.35
4
4
  Summary: Run commands on multiple hosts via SSH
5
5
  Home-page: https://github.com/yufei-pan/multiSSH3
6
6
  Author: Yufei Pan
@@ -603,10 +603,12 @@ mssh -q -- web1 grep -r TODO /srv
603
603
 
604
604
  ### `-ea, --extraargs EXTRAARGS` | _`DEFAULT_EXTRA_ARGS`_
605
605
  - Extra arguments to pass to the ssh / rsync / scp command. Put in one string for multiple arguments.
606
+ - The word after `-ea` is its value, also when it starts with `-`: `-ea '-p 2222'`, `-ea -A`. Quote an option together with its value: with `-ea -p 2222` the quotes were forgotten, and mssh stops, as it does for a password option there. ( mssh 6.34 and older need `-ea='-p 2222'`, which still works. )
606
607
  - The string is split into arguments as a shell would: quote an argument that contains spaces.
607
608
  - For a file copy with rsync, ssh's options that take a value go to the ssh that rsync runs, and the other arguments to rsync: the option followed by its value as the next word (`-o ConnectTimeout=5`, `-p 2222`, `-i FILE`, `-J HOST`, `-F FILE`, `-l USER`, ...), or `-oName=value` and `-pPORT`. scp takes ssh's options itself; ssh's `-p PORT` is given to it as `-P PORT`.
608
609
  - Example:
609
610
  ```bash
611
+ mssh -ea '-p 2222' allServers uptime
610
612
  mssh -ea="--delete" -f ./data/ allServers /tmp/data/
611
613
  mssh -ea="-o ProxyCommand='ssh -W %h:%p bastion'" allServers uptime
612
614
  ```
@@ -717,7 +719,7 @@ mssh -f ./data/ allServers /tmp/data/
717
719
 
718
720
  ### `-ia, --ipmi_args IPMI_ARGS` | _`DEFAULT_IPMI_ARGS`_
719
721
  - Extra arguments for ipmitool (or redfishtool) in `--ipmi` mode, in one string, placed as they are before the command. `-ea` arguments only go to ssh / scp / rsync.
720
- - Ex: `mssh -M -ia='-I lanplus' bmc[1-4] 'power status'` runs `ipmitool -H <address> -U <user> -E -I lanplus power status`, with the password in its environment. Use `=`, as the value starts with `-`.
722
+ - Ex: `mssh -M -ia '-I lanplus' bmc[1-4] 'power status'` runs `ipmitool -H <address> -U <user> -E -I lanplus power status`, with the password in its environment. The word after `-ia` is its value, also when it starts with `-`; quote an option together with its value ( mssh 6.34 and older need `-ia='-I lanplus'` ).
721
723
 
722
724
  ### `-im, --ipmi_method IPMI_METHOD` | _`DEFAULT_IPMI_METHOD`_
723
725
  - How `--ipmi` reaches a host:
@@ -972,7 +974,7 @@ Option names count in full only: `--user` is not `--username` and `-c` is not `-
972
974
 
973
975
  Putting the command after `--` is no substitute for quotes: every word after the hosts is a command of its own, so `mssh web1 -- bash -c 'echo hi'` runs `bash`, `-c` and `echo hi` one after another (when each word is a single word, as in `mssh web1 -- ls -la`, mssh asks whether to join them).
974
976
 
975
- Below is a sample help message output from multiSSH3 6.34 @ 2026-10-16 (no config file, 120 columns; the lines after the options list the programs found on that machine)
977
+ Below is a sample help message output from multiSSH3 6.35 @ 2026-10-16 (no config file, 120 columns; the lines after the options list the programs found on that machine)
976
978
 
977
979
  ```bash
978
980
  $ mssh -h
@@ -1003,7 +1005,7 @@ options:
1003
1005
  The identity file to use to connect to the hosts. Implies --use_key. Specify a folder for program to search for a key. Use option without value to use ~/.ssh/ (default: None)
1004
1006
  -uk, --use_key Attempt to use public key file to connect to the hosts. (default: False)
1005
1007
  -ea, --extraargs EXTRAARGS
1006
- Extra arguments to pass to the ssh / rsync / scp command. (Use -ia for ipmi arguments) Put in one string for multiple arguments.Use "=" ! Ex. -ea="--delete" (default: None)
1008
+ Extra arguments to pass to the ssh / rsync / scp command. (Use -ia for ipmi arguments) Put in one string for multiple arguments. The word after -ea is its value, also when it starts with "-": quote an option with its value. Ex. -ea "-p 2222 -A" (default: None)
1007
1009
  -11, --oneonone Run one corresponding command on each host. (default: False)
1008
1010
  -f, --file FILE The file to be copied to the hosts. Use -f multiple times to copy multiple files
1009
1011
  -s, -fs, --file_sync [FILE_SYNC]
@@ -1028,7 +1030,7 @@ options:
1028
1030
  -ip, --ipmi_password IPMI_PASSWORD
1029
1031
  The password to use to connect to the hosts via ipmi. (default: set, not shown)
1030
1032
  -ia, --ipmi_args IPMI_ARGS
1031
- The arguments passed to ipmitool to use to connect to the hosts via ipmi. (default: )
1033
+ The arguments passed to ipmitool to use to connect to the hosts via ipmi. The word after -ia is its value, also when it starts with "-": quote an option with its value. Ex. -ia "-I lanplus" (default: )
1032
1034
  -im, --ipmi_method IPMI_METHOD
1033
1035
  The method to use to connect to the hosts via ipmi. (default: ipmitool)
1034
1036
  -irlt, --ipmi_retry_lockout_time IPMI_RETRY_LOCKOUT_TIME
@@ -83,6 +83,7 @@ tests/test_run_on_hosts.py
83
83
  tests/test_run_on_hosts_modes.py
84
84
  tests/test_runtime_branch_matrix.py
85
85
  tests/test_runtime_dir.py
86
+ tests/test_shared_lines.py
86
87
  tests/test_smoke_optional.py
87
88
  tests/test_ssh_error_text.py
88
89
  tests/test_ssh_timeouts.py
@@ -90,9 +91,11 @@ tests/test_sshpass_exit.py
90
91
  tests/test_start_run_on_hosts.py
91
92
  tests/test_stopped_commands.py
92
93
  tests/test_stream_reader.py
94
+ tests/test_streamed_output.py
93
95
  tests/test_tui_ssh_notices.py
94
96
  tests/test_unavailable_hosts.py
95
97
  tests/test_underscore_settings.py
98
+ tests/test_waiting.py
96
99
  tests/test_wide_wrap.py
97
100
  tests/vt_grid.py
98
101
  tests/data/run_command_golden.json