Module: Ukiryu::Shell

Defined in:
lib/ukiryu/shell.rb,
lib/ukiryu/shell/sh.rb,
lib/ukiryu/shell/cmd.rb,
lib/ukiryu/shell/zsh.rb,
lib/ukiryu/shell/base.rb,
lib/ukiryu/shell/bash.rb,
lib/ukiryu/shell/dash.rb,
lib/ukiryu/shell/fish.rb,
lib/ukiryu/shell/tcsh.rb,
lib/ukiryu/shell/registry.rb,
lib/ukiryu/shell/unix_base.rb,
lib/ukiryu/shell/powershell.rb,
lib/ukiryu/shell/instance_cache.rb

Overview

Shell detection and management

Provides EXPLICIT shell detection with no fallbacks. If shell cannot be determined, raises a clear error.

Shell types are grouped by platform compatibility:

  • ‘unix` - All Unix-like shells (bash, zsh, fish, sh, dash, tcsh, ash, csh, ksh, + future shells)

  • ‘windows` - cmd.exe only

  • ‘powershell` - PowerShell Core on all platforms

Individual shell names are still supported for backward compatibility and are mapped to their appropriate platform group.

Defined Under Namespace

Classes: Base, Bash, Cmd, Dash, Fish, InstanceCache, PowerShell, Registry, Sh, Tcsh, UnixBase, Zsh

Constant Summary collapse

PLATFORM_GROUPS =

Platform-grouped shell types (new schema v1)

%i[unix windows powershell].freeze
INDIVIDUAL_SHELLS =

Individual shell types (backward compatibility)

%i[bash zsh fish sh dash tcsh powershell cmd].freeze
VALID_SHELLS =

All valid shell types (platform groups + individual shells for backward compatibility)

(PLATFORM_GROUPS + INDIVIDUAL_SHELLS).freeze
SHELL_TO_PLATFORM =

Map individual shells to their platform groups

{
  bash: :unix,
  zsh: :unix,
  fish: :unix,
  sh: :unix,
  dash: :unix,
  tcsh: :unix,
  # ash, csh, ksh, nushell, elvish, etc. would also map to :unix
  powershell: :powershell,
  pwsh: :powershell,
  cmd: :windows
}.freeze
PLATFORM_TO_SHELLS =

Reverse map: platform groups to individual shells

{
  unix: %i[bash zsh fish sh dash tcsh],
  windows: %i[cmd],
  powershell: %i[powershell pwsh]
}.freeze

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.current_shell=(value) ⇒ Object (writeonly)

Get or set the current shell (for explicit configuration)



103
104
105
# File 'lib/ukiryu/shell.rb', line 103

def current_shell=(value)
  @current_shell = value
end

Class Method Details

.all_validArray<Symbol>

Get list of all valid shells (platform groups + individual shells)

Returns:

  • (Array<Symbol>)

    list of valid shell symbols



116
117
118
# File 'lib/ukiryu/shell.rb', line 116

def all_valid
  VALID_SHELLS.dup
end

.available?(shell_sym) ⇒ Boolean

Check if a shell is available on the system

Parameters:

  • shell_sym (Symbol)

    the shell or platform group to check

Returns:

  • (Boolean)

    true if shell/platform group is available



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/ukiryu/shell.rb', line 194

def available?(shell_sym)
  # Platform groups
  return unix_shell_available? if shell_sym == :unix
  return powershell_available? if shell_sym == :powershell
  return Platform.windows? if shell_sym == :windows

  # Individual shells (backward compatibility)
  return false unless valid?(shell_sym)

  case shell_sym
  when :bash
    shell_available_on_unix?('bash') || bash_available_on_windows?
  when :zsh
    shell_available_on_unix?('zsh')
  when :fish
    shell_available_on_unix?('fish')
  when :sh
    shell_available_on_unix?('sh')
  when :dash
    shell_available_on_unix?('dash')
  when :tcsh
    shell_available_on_unix?('tcsh')
  when :pwsh
    powershell_available?
  else
    false
  end
end

.available_shellsArray<Symbol>

Get all shells available on the current system

Returns:

  • (Array<Symbol>)

    list of available shells



226
227
228
# File 'lib/ukiryu/shell.rb', line 226

def available_shells
  VALID_SHELLS.select { |shell| available?(shell) }
end

.class_for(name) ⇒ Class

Get shell class by name or platform group

For platform groups, returns the most appropriate shell class:

  • :unix → Bash (most common Unix shell)

  • :windows → Cmd

  • :powershell → PowerShell

Parameters:

  • name (Symbol)

    the shell name or platform group

Returns:

  • (Class)

    the shell class

Raises:

  • (UnknownShellError)

    if shell class not found



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/ukiryu/shell.rb', line 278

def class_for(name)
  # Check registry first (for custom shells)
  registered = Registry.lookup(name)
  return registered if registered

  # Built-in shells
  case name
  when :unix
    Bash # Most common Unix shell, all Unix shells share the same quoting rules
  when :windows
    Cmd
  when :powershell
    PowerShell
  when :bash
    Bash
  when :zsh
    Zsh
  when :fish
    Fish
  when :sh
    Sh
  when :dash
    Dash
  when :tcsh
    Tcsh
  when :pwsh
    PowerShell
  when :cmd
    Cmd
  else
    raise Ukiryu::Errors::UnknownShellError, "Unknown shell: #{name}"
  end
end

.detectSymbol

Detect the current shell

Returns:

  • (Symbol)

    :bash, :zsh, :fish, :sh, :powershell, or :cmd

Raises:

  • (UnknownShellError)

    if shell cannot be determined



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/ukiryu/shell.rb', line 234

def detect
  # Return explicitly configured shell if set
  return @current_shell if @current_shell

  # Check for test environment override (for CI testing with specific shells)
  test_shell = ENV['UKIRYU_TEST_SHELL']
  return test_shell.to_sym if test_shell && valid?(test_shell.to_sym)

  # Detect based on platform and environment
  if Platform.windows?
    detect_windows_shell
  else
    detect_unix_shell
  end
end

.executable_path(shell_sym) ⇒ String

Get shell executable path for the given shell name

Parameters:

  • shell_sym (Symbol)

    the shell symbol

Returns:

  • (String)

    the shell executable path

Raises:

  • (ArgumentError)

    if shell is not valid



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/ukiryu/shell.rb', line 152

def executable_path(shell_sym)
  return nil unless valid?(shell_sym)

  case shell_sym
  when :bash
    '/bin/bash'
  when :zsh
    '/bin/zsh'
  when :fish
    '/usr/bin/fish'
  when :sh
    '/bin/sh'
  when :dash
    '/bin/dash'
  when :tcsh
    '/bin/tcsh'
  when :powershell
    'pwsh' # PowerShell Core
  when :cmd
    'cmd'
  else
    raise ArgumentError, "Unknown shell: #{shell_sym}"
  end
end

.from_string(str) ⇒ Symbol

Convert string to shell symbol or platform group

Parameters:

  • str (String)

    the shell name string

Returns:

  • (Symbol)

    the shell symbol or platform group

Raises:

  • (ArgumentError)

    if shell name is invalid



182
183
184
185
186
187
188
# File 'lib/ukiryu/shell.rb', line 182

def from_string(str)
  shell_sym = str.to_s.downcase.to_sym
  return shell_sym if valid?(shell_sym)

  raise ArgumentError,
        "Invalid shell: #{str}. Valid: platform groups (#{PLATFORM_GROUPS.join(', ')}) or individual shells (#{INDIVIDUAL_SHELLS.join(', ')})"
end

.platform_group_for(shell_sym) ⇒ Symbol

Get the platform group for a given shell

Parameters:

  • shell_sym (Symbol)

    the shell symbol

Returns:

  • (Symbol)

    the platform group (:unix, :windows, :powershell)

Raises:

  • (ArgumentError)

    if shell is not valid



136
137
138
139
140
141
142
143
144
145
# File 'lib/ukiryu/shell.rb', line 136

def platform_group_for(shell_sym)
  return shell_sym if PLATFORM_GROUPS.include?(shell_sym)

  unless SHELL_TO_PLATFORM.key?(shell_sym)
    raise ArgumentError,
          "Unknown shell: #{shell_sym}. Valid shells: #{VALID_SHELLS.join(', ')}"
  end

  SHELL_TO_PLATFORM[shell_sym]
end

.register(name, shell_class) ⇒ Object

Register a custom shell class

Parameters:

  • name (Symbol)

    the shell name

  • shell_class (Class)

    the shell class (must inherit from Shell::Base)

Raises:

  • (ArgumentError)

    if shell_class is not a Shell::Base subclass



317
318
319
# File 'lib/ukiryu/shell.rb', line 317

def register(name, shell_class)
  Registry.register(name, shell_class)
end

.resetObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Reset cached shell detection (mainly for testing)



263
264
265
266
# File 'lib/ukiryu/shell.rb', line 263

def reset
  @current_shell = nil
  @shell_class = nil
end

.shell_classShell::Base

Get the shell class for the detected/configured shell

Returns:



253
254
255
256
257
258
# File 'lib/ukiryu/shell.rb', line 253

def shell_class
  @shell_class ||= begin
    shell_name = detect
    class_for(shell_name)
  end
end

.valid?(shell_sym) ⇒ Boolean

Check if a shell symbol is valid

Parameters:

  • shell_sym (Symbol)

    the shell symbol to check

Returns:

  • (Boolean)

    true if shell is valid



109
110
111
# File 'lib/ukiryu/shell.rb', line 109

def valid?(shell_sym)
  VALID_SHELLS.include?(shell_sym&.to_sym)
end

.valid_for_platformArray<Symbol>

Get shells valid for current platform (returns platform groups)

Returns:

  • (Array<Symbol>)

    list of valid shell groups for current platform



123
124
125
126
127
128
129
# File 'lib/ukiryu/shell.rb', line 123

def valid_for_platform
  if Platform.windows?
    %i[windows powershell unix] # Windows can run all three types
  else
    %i[unix powershell] # Unix can run Unix shells and PowerShell Core
  end
end