Created
March 14, 2023 06:50
-
-
Save spinal84/02405328ac0bb33a699e3231c20870ea to your computer and use it in GitHub Desktop.
Convert history files from bash to zsh and vice versa
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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