Skip to content

Instantly share code, notes, and snippets.

@spinal84
Created March 14, 2023 06:50
Show Gist options
  • Save spinal84/02405328ac0bb33a699e3231c20870ea to your computer and use it in GitHub Desktop.
Save spinal84/02405328ac0bb33a699e3231c20870ea to your computer and use it in GitHub Desktop.
Convert history files from bash to zsh and vice versa
#!/usr/bin/ruby
# Convert history files from bash to zsh and vice versa.
# Usage:
# cat ~/.bash_history | ruby bash2zsh_hist.rb > ~/.zsh_history
# cat ~/.zsh_history | ruby bash2zsh_hist.rb -r > ~/.bash_history
require 'set'
class Integer
# https://github.com/zsh-users/zsh/blob/dd13048b3b8cf710f44424ce9fedc2b56c31fde3/Src/utils.c#L4082-L4189
@@typtab = [0, 0x83, 0xa2,].to_set.merge(0x84..0x9c).merge(0x9d..0xa1)
# https://github.com/zsh-users/zsh/blob/dd13048b3b8cf710f44424ce9fedc2b56c31fde3/Src/ztype.h#L60
def imeta?
@@typtab === self
end
end
class String
META = 0x83
# https://github.com/zsh-users/zsh/blob/dd13048b3b8cf710f44424ce9fedc2b56c31fde3/Src/utils.c#L4755-L4840
def metafy
result = ''
each_byte do |c|
if c.imeta?
result << META.chr << (c ^ 32).chr
else
result << c.chr
end
end
result.force_encoding(encoding)
end
# https://github.com/zsh-users/zsh/blob/dd13048b3b8cf710f44424ce9fedc2b56c31fde3/Src/utils.c#L4865-L4888
def unmetafy
result = ''
meta = false
each_byte do |c|
if meta
result << (c ^ 32).chr
meta = false
elsif c == META
meta = true
else
result << c.chr
end
end
result.force_encoding(encoding)
end
def bash_timestamp?
start_with? '#' and length == 11 and self =~ /^#\d{10}$/
end
attr_reader :zsh_ts, :zsh_cmd
def zsh_timestamp?
return @have_zsh_ts if defined? @ts
@have_zsh_ts = start_with?(': ') && self =~ /^: (\d{10}):\d+;(.+)$/
@zsh_ts, @zsh_cmd = $1, $2 if @have_zsh_ts
@have_zsh_ts
rescue ArgumentError
warn "\nSkipping line #$. because of #$!\n#{inspect}"
end
end
class History
def self.bash2zsh
hist_entry = ''
timestamp = false
while gets
line = $_.chop
if line.bash_timestamp?
timestamp = true
puts hist_entry + "\n" unless hist_entry.empty?
hist_entry = ": %s:0;" % line[1..-1]
lines = 0
elsif timestamp
hist_entry << "\\\n" unless lines == 0
hist_entry << line.metafy
hist_entry << ' ' if line.end_with? "\\"
lines += 1
end
end
puts hist_entry + "\n" if timestamp and not hist_entry.empty?
end
def self.zsh2bash
hist_entry = ''
while gets
line = $_.chop.unmetafy
if hist_entry.empty? and line.zsh_timestamp?
ts, cmd = line.zsh_ts, line.zsh_cmd
if cmd.end_with? "\\"
cmd.slice!(-1)
cmd.slice!(-1) if cmd.end_with? "\\ "
hist_entry = "#%s\n%s\n" % [ts, cmd]
else
cmd.slice!(-1) if cmd.end_with? "\\ "
puts "#%s\n%s" % [ts, cmd]
end
elsif not hist_entry.empty?
if line.end_with? "\\"
line.slice!(-1)
line.slice!(-1) if line.end_with? "\\ "
hist_entry << line << "\n"
else
line.slice!(-1) if line.end_with? "\\ "
puts hist_entry + line + "\n"
hist_entry = ''
end
end
end
end
end
reverse = ARGV.shift if ARGV[0] == '-r' || ARGV[0] == '--reverse'
reverse ? History.zsh2bash : History.bash2zsh
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment