#$language = "Python" #$interface = "1.0" # SaveScreenTextToFile.py # Last Modified: 09 Jan, 2023 # - Initial Python version converted from VBScript example designed to # work with either Python 2 or Python 3 as the script engine. # # Description: # Saves text on the screen (or in the scrollback buffer, depending on args # provided to the script) to a unique file located in user's documents # folder (user can change the name of the file if desired, as they are # prompted to confirm the name -- unless the /Prompt:No arg is present). # # What gets saved to the file if no args are present depends on whether # or not there is anything on the screen currently selected. # - If there is any non-whitespace text selected on the screen, that # text is what gets "peeled off" into a separate file. # - If there is nothing selected on the screen, the entire screen gets # saved to a separate file. # # If the /mode:scrollback argument is specified when running the script # (for example, if you configure a button on SecureCRT's button bar to # run the script with argument of /mode:scrollback), then the entire screen # and all the contents of the scrollback buffer are saved to a file. # # The separate file that is saved defaults to a .txt file extension # and the default application assigned to open such files is launched # automatically for viewing/editing. # # Note that you can change the extension of the file to something different # (when prompted) as a quick way of changing which app gets launched to # view the data. For example, if you have primarily comma-separated values # selected or on the screen, rename the file to .csv in order to automatically # have Excel (or whichever app is assigned to handle .csv files) launched to # view the data (Mac and Windows). # # Demonstrates: # - How to use the crt.Dialog.FileOpenDialog API (SecureCRT 6.7 and later) # Note: Windows version of SecureCRT allows FileOpenDialog to return # a path to a file that doesn't yet exist... But Linux/Mac versions # of FileOpenDialog() require an existing file. So, the Browse() # function provides a workaround. # - How to use the Screen.Get2 property to get data from the screen where # individual lines are marked with CRLF line ending characters. # - One way of handling arguments passed to a script. import os import sys import subprocess import datetime import time import random import codecs # Be "tab safe" by getting a reference to the tab for which this script # has been launched: global objTab objTab = crt.GetScriptTab() global g_strMyDocs global g_strMode, g_bPromptForFilename g_strMyDocs = "" g_strMode = "" g_bPromptForFilename = True #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def Browse(strMessage, strButtonCaption, strDefaultPath, strFilter): strPlatform = sys.platform # Windows version of SecureCRT allows FileOpenDialog to return # a path to a file that doesn't yet exist... But Linux/Mac versions # of FileOpenDialog() require an existing file. So, use the nicer # interface in Windows, and on Linux/Mac, simply present an input # box that will allow for path to be changed, if desired. If you # are on Linux/Mac, and don't like the default path, simply change # the value of g_strMyDocs variable in SaveScreenTextToFile() below. if strPlatform == "win32": return crt.Dialog.FileOpenDialog( strMessage, strButtonCaption, strDefaultPath, strFilter) else: return crt.Dialog.Prompt( strMessage, strButtonCaption, strDefaultPath) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def Continue(strMsg): if crt.Dialog.MessageBox(strMsg, "Continue?", BUTTON_YESNO) != IDYES: return False else: return True #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def WriteToFile(strText, strFilePath): try: objFile = open(strFilePath,"a") except Exception as strError: MsgBox("Could not open log file with error: " + str(strError) + "\n\nNot capturing log file.") return 0 if strText != "": with codecs.open(strFilePath, "a", "utf-8") as objFile: objFile.write(strText) objFile.close return 1 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def GetDocumentsFolder(): objConfig = crt.OpenSessionConfiguration("Default") # Try and get at where the configuration folder is located. To achieve # this goal, we'll use one of SecureCRT's cross-platform path # directives that means "THE path this instance of SecureCRT # is using to load/save its configuration": ${VDS_CONFIG_PATH}. # First, let's use a session setting that we know will do the # translation between the cross-platform moniker ${VDS_CONFIG_PATH} # and the actual value... say, "Upload Directory V2" strOptionName = "Upload Directory V2" # Stash the original value, so we can restore it later... strOrigValue = objConfig.GetOption(strOptionName) # Now set the value to our moniker... objConfig.SetOption(strOptionName, "${VDS_USER_DATA_PATH}") # Make the change, so that the above templated name will get written # to the config... objConfig.Save() # Now, load a fresh copy of the config, and pull the option... so # that SecureCRT will convert from the template path value to the # actual path value: objConfig = crt.OpenSessionConfiguration("Default") strMyDocs = objConfig.GetOption(strOptionName) # Now, let's restore the setting to its original value objConfig.SetOption(strOptionName, strOrigValue) objConfig.Save() if sys.platform.startswith('linux'): strMyDocs = os.path.join(strMyDocs, "Documents") # Now return the Documents path return strMyDocs.replace("\\", "/") #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def SaveScreenTextToFile(): global g_strMyDocs, g_strMode, objTab, g_bPromptForFilename g_strMyDocs = GetDocumentsFolder() if g_strMode == "screen": # Transfer the data from the SecureCRT screen into a variable. Using # Screen.Get2() instead of Screen.Get() allows us to separate each # line with \r and \n characters so that lines appear in the file # the same way they appear in the SecureCRT window. strScreenData = objTab.Screen.Get2(1, 1, crt.Screen.Rows, crt.Screen.Columns) elif g_strMode == "scrollback": crt.Screen.SendSpecial("MENU_SELECT_ALL") strScreenData = crt.Screen.Selection elif g_strMode == "selection": strSelection = crt.Screen.Selection if IsBlockEmpty(strSelection): # Fall back to "screen" mode if there's nothing selected on the # screen. g_strMode = "screen" SaveScreenTextToFile() return strScreenData = strSelection # The screen/selection data as returned by SecureCRT will have EOL set to # \r\n. Convert to \n so that the data written to file will be platform # independently correct for ASII files. strScreenData = strScreenData.replace("\r\n", "\n") # Check to see if there's even anything that can be written to file. # If you desire to write out blank lines and tab characters to file, if # that is what the current screen contains, comment out the following # three lines of code (the entire if.. block) if IsBlockEmpty(strScreenData): crt.Dialog.MessageBox("Nothing available to save!") return strFilename = "" while True: if strFilename == "": # Create a unique filename for starters (user will be prompted to # change the name if the /Prompt:No arg is present as argument to # this script). while True: strFilename = os.path.join(g_strMyDocs, "SCRT_" + g_strMode + "_" + GetUniqueFileName()) if not os.path.isfile(strFilename): break strFilenameBefore = strFilename if g_bPromptForFilename: # Now prompt the user for the filename to use, defaulting to the # name determined above strFilename = Browse( "Specify Filename to Save", "Save", strFilename, "Text Files (*.txt)|*.txt||") if strFilename == "": return if os.path.isfile(strFilename): if crt.Dialog.MessageBox("Filename already exists!\n" + "\n\t" + strFilename + "\n\n" + "Do you wish to overwrite this file?", "Overwrite File?", BUTTON_YESNO) == IDYES: break else: break WriteToFile(strScreenData + "\n", strFilename) OpenPathInDefaultApp(strFilename) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def IsBlockEmpty(strBlockText): strTempValue = strBlockText strTempValue = strTempValue.strip() if strTempValue == "": return True else: return False #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def ProcessCommandLineArgs(): global g_strMode, g_bPromptForFilename if crt.Arguments.Count > 0: for nIndex in range(crt.Arguments.Count): strOrigArg = crt.Arguments.GetArg(nIndex) strArg = strOrigArg.lower() if strArg.find("/mode:") > -1: nPos = strArg.find(":") strArg = strArg[nPos + 1:] strArg = strArg.lower() strArg = strArg.strip() if strArg == "screen": g_strMode = "screen" elif strArg == "scrollback": g_strMode = "scrollback" elif strArg == "selection": g_strMode = "selection" else: crt.Dialog.MessageBox( "Unknown /mode: passed in as arg: " + strOrigArg + "\n\n" + "Allowed values are: screen | scrollback | selection") elif strArg.find("/prompt:") > -1: nPos = strArg.find(":") strArg = strArg[nPos + 1:] strArg = strArg.lower() strArg = strArg.strip() if strArg in ["off", "no", "0"]: g_bPromptForFilename = False elif strArg in ["on", "yes", "1"]: g_bPromptForFilename = True else: crt.Dialog.MessageBox( "Unknown /prompt: passed in as arg: " + strOrigArg + "\n\n" + "Allowed values are: on | off | yes | no | 1 | 0") if g_strMode == "": # If no mode args were passed, try to heuristically determine what to do # (screen or selection?) # If there's selected text on the screen, let's set mode to selection. # Otherwise, just use screen mode. if crt.Screen.Selection.strip() != "": g_strMode = "selection" else: g_strMode = "screen" #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def GetUniqueFileName(): # Create a unique file name, use current date/time info # to create a unique timestamp. strFilename = datetime.datetime.now().strftime("%Y%m%d_%H%M%S.%f")[:19] # ... return the unique filename as value of the function: return strFilename + ".txt" #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def OpenPathInDefaultApp(strFile): strPlatform = sys.platform crt.Session.SetStatusText("Platform: {0}".format(strPlatform)) crt.Sleep(200) try: if sys.platform.startswith('darwin'): subprocess.call(('open', strFile)) elif strPlatform == "win32": os.startfile(strFile) elif sys.platform.startswith('linux'): subprocess.call(('xdg-open', strFile)) else: MsgBox("Unknown operating system: {0}".format(os.name)) except Exception as objErr: MsgBox( ("Failed to open {0} with the default app.\n\n" + str(objErr).replace('\\\\', '\\').replace('u\'', '\'')).format(strFile)) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def Main(): # First order of work to do is handle any arguments that may have been # supplied to the script. We'll do this in a function to keep things # more clean. ProcessCommandLineArgs() SaveScreenTextToFile() Main()