Ruben Laguna’s blog

Merging Two TikiWiki’s

I’ve created a ruby script to merge the content of a TikiWiki into another one. This script will read the tiki_pages, tiki_history and tiki_links tables from the MySQL backend of the source TikiWiki and import the contents into the destination TikiWiki. The script is ‘safe’, meaning that it will not overwrite any page if it already exists in the destination. The history of the page will be merged as well if the page exists at the destination. The script doesn’t work with page attachments yet.

Here is the script, be sure to change the script parameters depending on your user/password on the databases (search oldtiki and newtiki variables in the script) and the charsets (search SET_CHARSET_NAME in the script).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
<span class='line'>#
</span><span class='line'># script to merge two tikiwiki s
</span><span class='line'>#
</span><span class='line'>require "rubygems"
</span><span class='line'>require "mysql"
</span><span class='line'>require "log4r"
</span><span class='line'>require "iconv"
</span><span class='line'>include Log4r
</span><span class='line'>
</span><span class='line'>def createInsert(dbh,  table,  row ) 
</span><span class='line'>      insertSt = "insert into #{table} "
</span><span class='line'>      col ="("
</span><span class='line'>      values = "VALUES("
</span><span class='line'>      
</span><span class='line'>      row.each { |key, value|
</span><span class='line'>        col += "#{key},"
</span><span class='line'>        if value and not value.empty?  then
</span><span class='line'>          values +=  "'#{dbh.escape_string(value)}',"
</span><span class='line'>        else 
</span><span class='line'>          values += "NULL,"
</span><span class='line'>        end    
</span><span class='line'>      }
</span><span class='line'>      col = col[0..-2] + ")"      
</span><span class='line'>      values = values[0..-2] + ")"
</span><span class='line'>      insertSt += col + " " + values
</span><span class='line'>      insertSt +";"
</span><span class='line'>      insertSt
</span><span class='line'>end
</span><span class='line'>
</span><span class='line'>$conflictNotif = {}
</span><span class='line'>def addToConflictNotifList (user, pagename) 
</span><span class='line'>  
</span><span class='line'>  pageList = $conflictNotif[user]
</span><span class='line'>  if not pageList then 
</span><span class='line'>    $conflictNotif[user] = []
</span><span class='line'>    pageList = $conflictNotif[user]
</span><span class='line'>  end
</span><span class='line'>  pageList &lt;&lt; pagename        
</span><span class='line'>end
</span><span class='line'>
</span><span class='line'>$notif = {}
</span><span class='line'>def addToNotifList (user, pagename) 
</span><span class='line'>  
</span><span class='line'>  pageList = $notif[user]
</span><span class='line'>  if not pageList then 
</span><span class='line'>    $notif[user] = []
</span><span class='line'>    pageList = $notif[user]
</span><span class='line'>  end
</span><span class='line'>  pageList &lt;&lt; pagename        
</span><span class='line'>end
</span><span class='line'>
</span><span class='line'>def printToFile (path, hash) 
</span><span class='line'>  fileConflicts   = File.open(path, File::WRONLY|File::TRUNC|File::CREAT) 
</span><span class='line'>  hash.each { |user, list|
</span><span class='line'>     fileConflicts.print "#{user}: "
</span><span class='line'>     fileConflicts.print list.join(',')
</span><span class='line'>     fileConflicts.print "\n"
</span><span class='line'>  }
</span><span class='line'>  fileConflicts.close
</span><span class='line'>end
</span><span class='line'>
</span><span class='line'>Log4r::Logger.root.level = Log4r::DEBUG
</span><span class='line'>
</span><span class='line'>l = Logger.new 'tiki_pages'
</span><span class='line'>l.outputters = Outputter.stdout,FileOutputter.new("tiki_pages", :filename => "tiki_pages.txt", :trunc => true, :level => Log4r::DEBUG)
</span><span class='line'>
</span><span class='line'>lh = Logger.new 'tiki_history'
</span><span class='line'>
</span><span class='line'>lh.outputters = Outputter.stdout,FileOutputter.new("tiki_history", :filename => "tiki_history.txt", :trunc => true, :level => Log4r::DEBUG)
</span><span class='line'>
</span><span class='line'>lm = Logger.new 'mysqlstatements'
</span><span class='line'>lm.outputters = FileOutputter.new("sqlfile", :filename => "commands.sql", :trunc => true, :level=>Log4r::DEBUG)
</span><span class='line'>
</span><span class='line'>l.debug "Starting migration script"
</span><span class='line'>
</span><span class='line'>oldtiki="sql1.example.com"
</span><span class='line'>olduser="root"
</span><span class='line'>olddbname="tiki"
</span><span class='line'>oldpwd="secret"
</span><span class='line'>
</span><span class='line'>newtiki="sql2.example.com"
</span><span class='line'>newuser="root"
</span><span class='line'>newdbname="rd_tiki_wiki"
</span><span class='line'>newpwd="secret"
</span><span class='line'>
</span><span class='line'>#select login,email from users_users;
</span><span class='line'>#mysql> select tiki_pages.pagename,users_users.email from tiki_pages,users_users where tiki_pages.user=users_users=login;
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>begin
</span><span class='line'>  #connect to the MySQL server
</span><span class='line'>  l.debug "trying to connect..."
</span><span class='line'>  dbhold = Mysql.init
</span><span class='line'>  dbhold.options(Mysql::SET_CHARSET_DIR, "/root/tikiWikiScript/charsets/")
</span><span class='line'>  dbhold.options(Mysql::SET_CHARSET_NAME, "utf8")
</span><span class='line'>  dbhold.real_connect(oldtiki,olduser, oldpwd,olddbname)
</span><span class='line'>  # get server version string and display it
</span><span class='line'>  l.info "#{oldtiki} mysql version: " + dbhold.get_server_info
</span><span class='line'>
</span><span class='line'>  dbhnew = Mysql.init
</span><span class='line'>  dbhnew.options(Mysql::SET_CHARSET_DIR, "/root/tikiWikiScript/charsets/")
</span><span class='line'>  dbhnew.options(Mysql::SET_CHARSET_NAME, "latin1")
</span><span class='line'>  dbhnew.real_connect(newtiki,newuser,newpwd,newdbname,3307)
</span><span class='line'>  l.info "#{newtiki} mysql version: " + dbhnew.get_server_info
</span><span class='line'>
</span><span class='line'>  l.info "retrieving all pagenames from old tiki..."
</span><span class='line'>  
</span><span class='line'>  res= dbhold.query("select * from tiki_pages");
</span><span class='line'>  
</span><span class='line'>  num_pages_old_tiki = res.num_rows
</span><span class='line'>  l.info "number of pages in old tiki : #{num_pages_old_tiki}"
</span><span class='line'>  insertions = 0
</span><span class='line'>  conflicts = 0
</span><span class='line'>  history_updates = 0
</span><span class='line'>  history_version_conflicts = 0
</span><span class='line'>  while row = res.fetch_hash do
</span><span class='line'>    #l.debug row
</span><span class='line'>    pagename = row["pageName"]
</span><span class='line'>    lastModif = row["lastModif"]
</span><span class='line'>    user = row["user"]
</span><span class='line'>    creator = row["creator"]
</span><span class='line'>    
</span><span class='line'>    l.debug "checking pageName='#{pagename}'"
</span><span class='line'>    escapePageName = dbhnew.escape_string(pagename)
</span><span class='line'>    l.debug "escapePageName='#{escapePageName}'"
</span><span class='line'>    query = "select lastModif,user,creator from tiki_pages where pageName='#{escapePageName}'"
</span><span class='line'>    res2 = dbhnew.query(query)
</span><span class='line'>    
</span><span class='line'>    if (res2.num_rows == 0) then
</span><span class='line'>      pageid =  dbhnew.query("select max(page_id)+1 from tiki_pages").fetch_row[0]
</span><span class='line'>      
</span><span class='line'>      l.info "Creating page '#{pagename}' with pageid #{pageid}"        
</span><span class='line'>      
</span><span class='line'>      row["page_id"] = pageid;
</span><span class='line'>      insertSt = createInsert(dbhnew,  "tiki_pages", row ) 
</span><span class='line'>      lm.debug "#{insertSt}"   
</span><span class='line'>      
</span><span class='line'>      dbhnew.query "#{insertSt}"   
</span><span class='line'>      
</span><span class='line'>      addToNotifList( user, pagename);
</span><span class='line'>      addToNotifList( creator, pagename) unless user == creator
</span><span class='line'>      insertions += 1
</span><span class='line'>    else
</span><span class='line'>      if (res2.num_rows > 1) then
</span><span class='line'>        l.error "database invariant violated: entry for pagename #{pagename} not found in the new tiki"
</span><span class='line'>        fail
</span><span class='line'>      end
</span><span class='line'>      row2 = res2.fetch_hash
</span><span class='line'>      lastModif2 = row2["lastModif"]
</span><span class='line'>      l.debug "Comparing last modification of page #{pagename} in old tiki with same page in new tiki"
</span><span class='line'>      if (lastModif > lastModif2) then                
</span><span class='line'>        l.warn "pagename \"#{pagename}\" is newer in #{oldtiki} than in #{newtiki}"
</span><span class='line'>        l.warn "we should send an email to #{user} and #{creator}"
</span><span class='line'>        
</span><span class='line'>        addToConflictNotifList( user, pagename)
</span><span class='line'>        addToConflictNotifList( creator, pagename) unless user == creator
</span><span class='line'>        
</span><span class='line'>        conflicts += 1
</span><span class='line'>      end
</span><span class='line'>      
</span><span class='line'>    end
</span><span class='line'>    res2.free
</span><span class='line'>    historyRes =  dbhold.query("select * from tiki_history where pageName='#{escapePageName}' ORDER BY version ASC")
</span><span class='line'>    anyHistoryUpdate = false;
</span><span class='line'>    while oldTikiHistoryEntry = historyRes.fetch_hash do
</span><span class='line'>      m = oldTikiHistoryEntry["lastModif"]
</span><span class='line'>      historyRes2 = dbhnew.query("select pageName from tiki_history where pageName='#{escapePageName}' and lastModif='#{m}'")
</span><span class='line'>      if (historyRes2.num_rows == 0) then
</span><span class='line'>      # this history entry was not present we must add new entry
</span><span class='line'>        version = oldTikiHistoryEntry["version"]        
</span><span class='line'>        lh.info "adding version #{version} last updated on #{oldTikiHistoryEntry["lastModif"]} to tiki_history for page '#{pagename}'"
</span><span class='line'>        historyRes3 = dbhnew.query("select lastModif from tiki_history where pageName='#{escapePageName}' and version='#{version}'")
</span><span class='line'>        if (historyRes3.num_rows == 0) then
</span><span class='line'>          lh.debug "version: #{version} not present in #{newtiki}. Insert the entry"
</span><span class='line'>        else
</span><span class='line'>          lh.debug "version: #{version} of '#{pagename}' already exists in #{newtiki}. Now we have to insert the entry in the middle."
</span><span class='line'>          dbhnew.query("update tiki_history set version=version+1 where pageName='#{escapePageName}' and version >= '#{version}' ORDER BY version DESC")
</span><span class='line'>          history_version_conflicts += 1
</span><span class='line'>        end
</span><span class='line'>        insertSt = createInsert(dbhnew, "tiki_history",  oldTikiHistoryEntry ) 
</span><span class='line'>        lm.info "#{insertSt}"
</span><span class='line'>        dbhnew.query "#{insertSt}"
</span><span class='line'>        history_updates += 1
</span><span class='line'>        anyHistoryUpdate = true;
</span><span class='line'>      else 
</span><span class='line'>        lh.debug "The history entry with modification date #{m} of pageName=#{pagename} was already present in the tiki_history of #{newtiki}. Skipping it"
</span><span class='line'>      end
</span><span class='line'>    end
</span><span class='line'>    l.info "History of page #{pagename} updated." if anyHistoryUpdate
</span><span class='line'>    historyRes.free
</span><span class='line'>  end
</span><span class='line'>  l.info "number of pages in old tiki : #{num_pages_old_tiki}"
</span><span class='line'>  l.info "number of insertions in the new wiki #{insertions}"
</span><span class='line'>  l.info "number of conflicts in the new wiki #{conflicts}"
</span><span class='line'>  l.info "number of history updates #{history_updates}"
</span><span class='line'>  l.info "number of history version conflicts #{history_version_conflicts}"
</span><span class='line'>  
</span><span class='line'>  dateString = Time.now.to_s
</span><span class='line'>  filename = "conflicts"+dateString+".txt"
</span><span class='line'>  printToFile(filename, $conflictNotif)
</span><span class='line'>  l.info "#{filename} created"
</span><span class='line'>  filename = "notifications"+dateString+".txt"
</span><span class='line'>  printToFile(filename, $notif)
</span><span class='line'>  l.info "#{filename} created"
</span><span class='line'>  
</span><span class='line'>  res.free
</span><span class='line'>  
</span><span class='line'>  #Update tiki_links that is responsible for the backlinks feature
</span><span class='line'>  link_insertions = 0
</span><span class='line'>  l.debug "retrieve all links"
</span><span class='line'>  linkRes = dbhold.query "select * from tiki_links"
</span><span class='line'>  while linkRow = linkRes.fetch_hash do
</span><span class='line'>    fromPage = dbhnew.escape_string(linkRow["fromPage"])
</span><span class='line'>    toPage = dbhnew.escape_string(linkRow["toPage"])
</span><span class='line'>    #check if this link already exists
</span><span class='line'>    checkRes = dbhnew.query "select * from tiki_links where fromPage='#{fromPage}' and toPage='#{toPage}'"
</span><span class='line'>    if (checkRes.num_rows == 0) then 
</span><span class='line'>      insertSt = createInsert(dbhnew, "tiki_links", linkRow)
</span><span class='line'>      lm.info "#{insertSt}"
</span><span class='line'>      dbhnew.query insertSt
</span><span class='line'>      link_insertions += 1      
</span><span class='line'>    end    
</span><span class='line'>    checkRes.free
</span><span class='line'>  end
</span><span class='line'>  linkRes.free
</span><span class='line'>  l.info "number of link_insertions #{link_insertions}"
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>rescue Mysql::Error => e
</span><span class='line'>  l.error "Error code: #{e.errno}"
</span><span class='line'>  l.error "Error message: #{e.error}"
</span><span class='line'>  l.error "Error SQLSTATE: #{e.sqlstate}" if e.respond_to?("sqlstate")
</span><span class='line'>ensure
</span><span class='line'>  # disconnect from server
</span><span class='line'>  l.debug "disconnecting from database"
</span><span class='line'>  dbhold.close if dbhold
</span><span class='line'>  dbhnew.close if dbhnew
</span><span class='line'>end</span>

The code is also available as a gist

You’ll need to install rubygems and Mysql module gem first.

Inspecting Tomcat HTTPS Connection With Wireshark

Wireshark allows you to inspect SSL connection as long as you have the corresponding private key of the server side. You can read the details here. But if you are using java and tomcat you’ll probably have the certificate and private key stored in a JKS keystore so how can you extract the key in the right format for WireShark?

First of all, keytool doesn’t allow you to extract the private key from a keystore. So you need external help. I use the DumpPrivateKey.java which is a modified version on the DumpPrivateKey found in this forum post.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<span class='line'>import java.io.FileInputStream;
</span><span class='line'>import java.security.Key;
</span><span class='line'>import java.security.KeyStore;
</span><span class='line'>
</span><span class='line'>import sun.misc.BASE64Encoder;
</span><span class='line'>
</span><span class='line'>/**
</span><span class='line'> * This is an utility program that reads the keystore file specified in the
</span><span class='line'> * parameter and dumps to the standard output the private key encoded in Base64
</span><span class='line'> * 
</span><span class='line'> */
</span><span class='line'>public class DumpPrivateKey {
</span><span class='line'>  /**
</span><span class='line'>   * Main method. Invoked from command line. This method open the jks file
</span><span class='line'>   * specified in the parameter to get the private key, transforms it in
</span><span class='line'>   * Base64 format and write it to the standard output. @Usage@:
</span><span class='line'>   * java DumpPrivateKey keystore.jks alias storepassword keypassword
</span><span class='line'>   * 
</span><span class='line'>   * @param args
</span><span class='line'>   *            List of strings containing the input parameters.
</span><span class='line'>   */
</span><span class='line'>  static public void main(String[] args) {
</span><span class='line'>    try {
</span><span class='line'>      if (args.length != 4) {
</span><span class='line'>        System.err
</span><span class='line'>            .println("Usage java DumpPrivateKey keystore.jks alias storepassword keypassword");
</span><span class='line'>        System.exit(1);
</span><span class='line'>      }
</span><span class='line'>      KeyStore ks = KeyStore.getInstance("jks");
</span><span class='line'>      String keystore = args[0];
</span><span class='line'>      String alias = args[1];
</span><span class='line'>      String storepass = args[2];
</span><span class='line'>      String keypass = args[3];
</span><span class='line'>
</span><span class='line'>      ks.load(new FileInputStream(keystore), storepass.toCharArray());
</span><span class='line'>      Key key = ks.getKey(alias, keypass.toCharArray());
</span><span class='line'>      if (key == null) {
</span><span class='line'>        System.err.println("No key found for alias:" + alias
</span><span class='line'>            + " and keypass:" + keypass);
</span><span class='line'>        System.exit(1);
</span><span class='line'>      }
</span><span class='line'>
</span><span class='line'>      BASE64Encoder myB64 = new BASE64Encoder();
</span><span class='line'>      String b64 = myB64.encode(key.getEncoded());
</span><span class='line'>
</span><span class='line'>      System.out.println("-----BEGIN PRIVATE KEY-----");
</span><span class='line'>      System.out.println(b64);
</span><span class='line'>      System.out.println("-----END PRIVATE KEY-----");
</span><span class='line'>    } catch (Exception e) {
</span><span class='line'>      e.printStackTrace();
</span><span class='line'>    }
</span><span class='line'>  }
</span><span class='line'>}</span>

The code is also available as a gist

Issuing the command

The command java -cp . DumpPrivateKey wwwserver.jks tomcat changeit changeit >server.key will export the private key to server.key but you need to convert this key format to the format supported by wireshark. You can do that with openssl pkcs8 -inform PEM -nocrypt -in server.key -out server.rsa.key

Then you can use the server.rsa.key in WireShark →Edit →Preferences→Protocol→SSL →rsa key file list → 192.168.0.4,443,http,c:\server.rsa.key.

Hope it works for you!

Eclipse Preference Page Extension Wizard Troubleshooting

Today I tried to add a Preference Page to an RCP application that I’m working on with no luck. After executing the Extension → Add → Extension Wizards → Preference Page I end up with 3 new classes. But 2 of then have compilation errors, they refer to an unexisting PreferenceClass. After googling a bit I found this message in a mailing list describing the same exact problem:

>Hello,

>I ‘ve added preference page in my application using plugin.xml extensions template. Then, in a new package, Eclipse created 3 classes (Template preferences page extending FieldEditorPreferencePage, a preference constants class and a preference initializer class).
>The template preferences page uses a PreferenceClass which doesn’t already exists… I suppose this class has to initialize the preference store, in the preference template class constructor, unsing the plugin instance of the product :
>"setPreferenceStore(PreferenceClass.getDefault().getPreferenceStore());"
>I’ve tried to extend the AbstractUIPlugin (I’ve read in differents articles from eclipse.org) to catch the plugin instance when it is created, but without succes (when to catch this single instance during startup of eclispe core runtime ?). Then I’ve found the existance of the WorkBenchPlugin class
which holds this static instance, I used it, discarding PreferenceClass , but Eclispe tells me that the use of this class is discouraged.
>Why ? How is it possible to store the plugin instance at startup ?
>Thanks a lot for your help,
>frank

Then I found the solution, if you haven’t defined an Activator for the plugin yet it will not work. After creating an Activator for the plugin and rerunning the wizard all went fine.

Sony Ericsson K750 USB Charging

If you’re frustated trying to charge your K750 from USB with no luck. Try to install the drivers first, it actually makes a difference. The K750 wont recharge the battery if the drivers are not installed. At least that worked for me in Windows 2000.

sony ericsson 750

Synchronize Bash History Among Different Machines

Today I finally decide to make a script to keep my bash_history synchronized among all the machines I use. It’s a simple script that retrieves all the .bash_history files from all my machines, merges all the files into one, sort it, removes the duplicated lines and write it back to all the machines. The only drawback is that the original ordering of the history files is lost due to the alphabetical sorting step (needed to remove the duplicated lines).

WARNING: Make sure that your history file size limit is big enough to hold all the combined history files. Otherwise you’ll lose some of the entries. You can go to this post for reference on how to change the history file limits

Here’s the script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<span class='line'>echo copying local history file
</span><span class='line'>history -a
</span><span class='line'>cp .bash_history full_history
</span><span class='line'>
</span><span class='line'>HOSTS="machine1.example.com machine2.example.com"
</span><span class='line'>for i in $HOSTS; do
</span><span class='line'>  echo copying history file from $i
</span><span class='line'>  scp $i:~/.bash_history tmp_history.txt
</span><span class='line'>  cat tmp_history.txt >>full_history
</span><span class='line'>  wc -l tmp_history.txt
</span><span class='line'>  wc -l full_history
</span><span class='line'>done
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>echo sorting the new history file and removing duplicates
</span><span class='line'>sort full_history|uniq >uniq_history
</span><span class='line'>rm full_history
</span><span class='line'>
</span><span class='line'>echo replacing history file with the new one
</span><span class='line'>mv uniq_history .bash_history
</span><span class='line'>echo reloading bash history from file
</span><span class='line'>history -c
</span><span class='line'>history -r
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>wc -l .bash_history
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>for i in $HOSTS; do
</span><span class='line'>  echo backing up .bash_history in $i
</span><span class='line'>  DATE=`date '+%Y%m%d%H%M%S'`
</span><span class='line'>  ssh $i "cp ~/.bash_history ~/.bash_history$DATE"
</span><span class='line'>  echo replacing .bash_history in $i
</span><span class='line'>  scp .bash_history $i:~/.bash_history
</span><span class='line'>done</span>

The code is also available as a gist

Copyright © 2015 - Ruben Laguna - Powered by Octopress