<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40"><head><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=us-ascii"><meta name=Generator content="Microsoft Word 15 (filtered medium)"><style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0cm;
        margin-bottom:.0001pt;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;
        mso-fareast-language:EN-US;}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:#0563C1;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:#954F72;
        text-decoration:underline;}
span.EmailStyle17
        {mso-style-type:personal-compose;
        font-family:"Calibri",sans-serif;
        color:windowtext;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-family:"Calibri",sans-serif;
        mso-fareast-language:EN-US;}
@page WordSection1
        {size:612.0pt 792.0pt;
        margin:72.0pt 72.0pt 72.0pt 72.0pt;}
div.WordSection1
        {page:WordSection1;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]--></head><body lang=EN-CA link="#0563C1" vlink="#954F72"><div class=WordSection1><p class=MsoNormal>Spice protocols are SO much better/faster/more fucntional/… (thank you) than alternatives.<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>I could not see where to put this on your forum so if you want it, please feel free to add it wherever you want - no attribution required.<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>I have enclosed below a Perl script I wrote - using material from various online forums and adding my code - that allows a Windows user to  use Spice protocols to access a Proxmox VM (in my case Linux Mint) by launching a Shortcut on the Windows machine.  <o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>This way you do NOT have to launch the Proxmox GUI and use the Proxmox Console to generate the SPICE certificate to use the Spice connection.<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>The script is initiated via a shortcut on the Windows machine that points to the script.<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>The script creates an SSH connection between the WIndows machine and the Proxmox VM, generates the Spice certificate and launches the Spice client (Virtual Viewer) on the Windows machine.<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>I installed Strawberry Perl and Remote Viewer on the Windows machine for this to work.<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>It makes it a LOT easier to access the Linux Mint VM, instead of using the intermediate Proxmox GUI.<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>I you want to use/post/share this, feel free.  No attribution required. See below.<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>Richard<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal><span style='mso-fareast-language:EN-CA'>Richard Cantin<o:p></o:p></span></p><p class=MsoNormal><span style='mso-fareast-language:EN-CA'><a href="mailto:richard.cantin@ayuda.ca">richard.cantin@ayuda.ca</a><o:p></o:p></span></p><p class=MsoNormal><span style='mso-fareast-language:EN-CA'>(519) 957-2414 [home]<o:p></o:p></span></p><p class=MsoNormal><span style='mso-fareast-language:EN-CA'>(519) 574-9401 [mobile]<o:p></o:p></span></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>===================================================================<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>#!C:\Strawberry\perl\bin\perl.exe<o:p></o:p></p><p class=MsoNormal>###!/usr/bin/perl<o:p></o:p></p><p class=MsoNormal>###!perl<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># See perldoc.perl.org for information on Perl<o:p></o:p></p><p class=MsoNormal># # comments out a line<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># This script allows a Windows user to access a Proxmox VM via Spice protocols<o:p></o:p></p><p class=MsoNormal># WITHOUT having to launch the Proxmox GUI to use the Proxmox Console<o:p></o:p></p><p class=MsoNormal># by simply double-clicking on a Shortcut on the Windows PC<o:p></o:p></p><p class=MsoNormal>#<o:p></o:p></p><p class=MsoNormal># This script is installed on a Windows PC<o:p></o:p></p><p class=MsoNormal># It requires <o:p></o:p></p><p class=MsoNormal>#             a Perl interpreter<o:p></o:p></p><p class=MsoNormal>#                            ( I use Strawberry Perl at https://strawberryperl.com )<o:p></o:p></p><p class=MsoNormal>#             a module on the PC to enable SSH connections via script<o:p></o:p></p><p class=MsoNormal>#                            ( I use Net::SSH::Perl installed with "cpan install Net::SSH::Perl" )<o:p></o:p></p><p class=MsoNormal>#             a Spice-compatible Remote Viewer Windows package <o:p></o:p></p><p class=MsoNormal>#                            (I use the msi installer for virt-viewer at https://virt-manager.org/download )<o:p></o:p></p><p class=MsoNormal>#             a Proxmox VM with the Spice client enabled<o:p></o:p></p><p class=MsoNormal>#                            ( I use Linux Mint as a Proxmox VM )<o:p></o:p></p><p class=MsoNormal>#             a Public/Private key pair enabling SSH connection from the Windows to the Proxmox host<o:p></o:p></p><p class=MsoNormal>#                            ( I use PuttyGen to create the keys on the PC - Export the Private key in OpenSSH format )<o:p></o:p></p><p class=MsoNormal>#<o:p></o:p></p><p class=MsoNormal># Install this script on the Windows PC<o:p></o:p></p><p class=MsoNormal># Put the public key onto the Proxmox host and manually SSH to the Proxmox host from the Wondows PC<o:p></o:p></p><p class=MsoNormal>#   to establish an entry in the "kown-hosts" file on the Windows PC<o:p></o:p></p><p class=MsoNormal># Set the script parameters specific to your installation in the variables section below<o:p></o:p></p><p class=MsoNormal># Point a Shortcut to this script<o:p></o:p></p><p class=MsoNormal># Launch the shortcut<o:p></o:p></p><p class=MsoNormal>#<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># ##############################################################<o:p></o:p></p><p class=MsoNormal># Installation parameters<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># Establish Parameters for the connection and command to use for fetching the SPICE ticket<o:p></o:p></p><p class=MsoNormal># Replace the values enclosed in <> and delete the enclosing brackets < and ><o:p></o:p></p><p class=MsoNormal>#<o:p></o:p></p><p class=MsoNormal># The address of the Proxmox host (NOT the VM to which you want to connect)<o:p></o:p></p><p class=MsoNormal>my $host = '<FQDN or IP Address of Proxmox Host>';<o:p></o:p></p><p class=MsoNormal># Set the Port number used by the remote Proxmox host to accept SSH connections (usually 22)<o:p></o:p></p><p class=MsoNormal>my $port_number = 22;<o:p></o:p></p><p class=MsoNormal># Set the SSH protocol version (usually 2) accepted by the remote Proxmox host<o:p></o:p></p><p class=MsoNormal>my $protocol_selection = '2';<o:p></o:p></p><p class=MsoNormal># Set the (non-root) remote user username for the VM (NOT the Proxmox host)<o:p></o:p></p><p class=MsoNormal>my $non_root_user = '<Non-root username on VM>';<o:p></o:p></p><p class=MsoNormal># Set the (non-root) remote user password for the VM (NOT the Proxmox host)<o:p></o:p></p><p class=MsoNormal>my $non_root_user_password = "<Non-root password on VM>";<o:p></o:p></p><p class=MsoNormal># Set the name of the Proxmox Node (the host or physical machine) <o:p></o:p></p><p class=MsoNormal>my $node_name = "<Name of Proxmox Node>";<o:p></o:p></p><p class=MsoNormal># Set the VM Number of the VM to which you want to connect via Spice<o:p></o:p></p><p class=MsoNormal>my $vm_number = <VM Number of VM to which you want to connect via Spice>;<o:p></o:p></p><p class=MsoNormal># Set the location of the Private key on the Windows PC (in OpenSSH format)<o:p></o:p></p><p class=MsoNormal># Remember to use Windows directory delimiters (backslash) and precede with a backslash to make it a literal value<o:p></o:p></p><p class=MsoNormal>my @identity_file_location = ( "<DiskID:\\Full\\Path\\To\\Private\\Key.pem>" );<o:p></o:p></p><p class=MsoNormal># Set the filename and path for edited file containing Spice Certificate<o:p></o:p></p><p class=MsoNormal># The file is created on a temporary basis and deleted once used<o:p></o:p></p><p class=MsoNormal># File suffix is .vv<o:p></o:p></p><p class=MsoNormal>my $output_filename_with_path = "<DiskID:\\Full\\Path\\To\\Temporary\\Storage\\Of\\Spice\\Certificate.vv>";<o:p></o:p></p><p class=MsoNormal># Set the location of the Remote viewer application that will use the ticket / configuration file generated<o:p></o:p></p><p class=MsoNormal># Usually looks something like the path shown - adjust to your specifics<o:p></o:p></p><p class=MsoNormal>my $path_to_remoteviewer_program = 'C:\Program Files\VirtViewer v11.0-256\bin\remote-viewer.exe';<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># ##############################################################<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># Nothing after this normally needs customizing<o:p></o:p></p><p class=MsoNormal>#   unless your spice generator has a different format for the certificates it generates<o:p></o:p></p><p class=MsoNormal>#  (ie the layout uses something other than | as a separator)<o:p></o:p></p><p class=MsoNormal># Try this as is and if it works, great, but if not<o:p></o:p></p><p class=MsoNormal>#             Set the parameters above for your installation<o:p></o:p></p><p class=MsoNormal>#             Comment out the line near the end of this script "system : '$path_to_remoteviewer_program" ...'<o:p></o:p></p><p class=MsoNormal>#             Run this script and use a text editor to view the certificate<o:p></o:p></p><p class=MsoNormal>#                            It will be stored at the location specified above in $output_filename_with_path<o:p></o:p></p><p class=MsoNormal>#             Adjust the RegEx below to suit<o:p></o:p></p><p class=MsoNormal>#             Restore the line near the end of this script "system : '$path_to_remoteviewer_program" ...'<o:p></o:p></p><p class=MsoNormal>#             Run this script<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># Set the debug flag to true<o:p></o:p></p><p class=MsoNormal>my $debug_flag = 1;<o:p></o:p></p><p class=MsoNormal># Include this whenever creating code; If you want to comment it out for production, your call<o:p></o:p></p><p class=MsoNormal>use warnings;<o:p></o:p></p><p class=MsoNormal># You may want to comment this out for production (resource usage) but use for development<o:p></o:p></p><p class=MsoNormal>use diagnostics;<o:p></o:p></p><p class=MsoNormal># Use this to ensure you properly declare variables prior to their use and reserve space for them<o:p></o:p></p><p class=MsoNormal>use strict;<o:p></o:p></p><p class=MsoNormal># establish the encoding protocol to use for all script elements<o:p></o:p></p><p class=MsoNormal>use utf8;<o:p></o:p></p><p class=MsoNormal># Set the STDIN STDOUT and STDERR to show with UTF-8 formatting <o:p></o:p></p><p class=MsoNormal># This helps with debugging with print statements<o:p></o:p></p><p class=MsoNormal>use open ":std", ":encoding(UTF-8)";<o:p></o:p></p><p class=MsoNormal># Enable the module on the PC that will setup SSH connections<o:p></o:p></p><p class=MsoNormal># Make sure the module is installed and if not, install it with "cpan install Net::SSH::Perl"<o:p></o:p></p><p class=MsoNormal>use Net::SSH::Perl;<o:p></o:p></p><p class=MsoNormal># Establish Parameters for the conversion of the raw ticket to a format remoteviewer can use<o:p></o:p></p><p class=MsoNormal># Tell the system what encoding you will be using: more necessary for read but a good habit to get into<o:p></o:p></p><p class=MsoNormal>my $encoding = ":encoding(UTF-8)";<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># =======================================================<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># Instantiate the SSH connection object using the method enabled above NET:SSH:Perl<o:p></o:p></p><p class=MsoNormal>my $ssh_connection = Net::SSH::Perl->new( $host , debug => $debug_flag , port => $port_number , protocol => $protocol_selection , identity_files => \@identity_file_location );<o:p></o:p></p><p class=MsoNormal># Or, with some options that are not required for basic Debian access but some shared hosting require<o:p></o:p></p><p class=MsoNormal># my $ssh_connection = Net::SSH::Perl->new( $host , debug => $debug_flag , port => $port_number , protocol => $protocol_selection , identity_files => \@identity_file_location , options => [ "HostKeyAlgorithms +ssh-rsa,ssh-ed25519" , "PubkeyAcceptedAlgorithms +ssh-rsa,ssh-ed25519" ] );<o:p></o:p></p><p class=MsoNormal># Or, to have it work with user/password (when user/password is enabled)<o:p></o:p></p><p class=MsoNormal># my $ssh_connection = Net::SSH::Perl->new( $host , debug => $debug_flag , port => $port_number , protocol => $protocol_selection , options => [ "HostKeyAlgorithms +ssh-rsa,ssh-ed25519" , "PubkeyAcceptedAlgorithms +ssh-rsa,ssh-ed25519" ] );<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># Login to the remote host<o:p></o:p></p><p class=MsoNormal># If the host system has previously been associated with a different "known_host"<o:p></o:p></p><p class=MsoNormal># make sure to delete the previous host entry in Windows<o:p></o:p></p><p class=MsoNormal># For Windows 7 find this by entering %USERPROFILE% in the search bar and looking in the .ssh directory<o:p></o:p></p><p class=MsoNormal># I disable remote SSH with root for security purposes so you need to login with a non-root user<o:p></o:p></p><p class=MsoNormal># and then use sudo to perform superuser commands like pvesh<o:p></o:p></p><p class=MsoNormal>$ssh_connection->login($non_root_user);<o:p></o:p></p><p class=MsoNormal># Or, to have it work with user/password (when user/password is enabled)<o:p></o:p></p><p class=MsoNormal># $ssh_connection->login($non_root_user,$non_root_user_password);<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># Issue the command at the remote host to generate the SPICE ticket and put it into a variable called $output<o:p></o:p></p><p class=MsoNormal># I know storing a password in a script and sending it is a security issue<o:p></o:p></p><p class=MsoNormal># If someone else wants to use Expect or React for making an interactive script, be my guest<o:p></o:p></p><p class=MsoNormal># This script uses key-pair to make the encrypted connection, then sends the password so I am ok with the transmission<o:p></o:p></p><p class=MsoNormal># Yes, storage of password in a readable text file is risky; I password protect my PC and do not allow others access<o:p></o:p></p><p class=MsoNormal>my $command = ( "echo $non_root_user_password | sudo -S pvesh create /nodes/$node_name/qemu/$vm_number/spiceproxy" );<o:p></o:p></p><p class=MsoNormal>my ($output, $error_message, $exit_code) = $ssh_connection->cmd( "$command" );<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># Read the raw ticket content, edit the content to match what remote-viewer is looking for<o:p></o:p></p><p class=MsoNormal># and write the edited content to a file on the system that is hosting the script<o:p></o:p></p><p class=MsoNormal># We will use this edited content file as the configuration settings when we launch remoteviewer<o:p></o:p></p><p class=MsoNormal># Create the file handler, with read and/or write permission (include the $encoding as shown)<o:p></o:p></p><p class=MsoNormal>#             The '>' designator tells the system this file handler is for writing<o:p></o:p></p><p class=MsoNormal>#             To Read, use "< $encoding"<o:p></o:p></p><p class=MsoNormal>#             To Read and Write use "+< $encoding"<o:p></o:p></p><p class=MsoNormal>#             To Read and Write by deleting existing content in a file (truncating) use "+> $encoding"<o:p></o:p></p><p class=MsoNormal>#             To Read and Write by appending to existing contents of a file use "+>> $encoding"<o:p></o:p></p><p class=MsoNormal># If the file deos not already exist, it will be created<o:p></o:p></p><p class=MsoNormal># Note: the $! system variable contains the error code for the failure.  If $! is empty, there was no error<o:p></o:p></p><p class=MsoNormal>open ( my $raw_content_handler , "< $encoding" , \$output ) or die "Could not open the raw content due to $! \r\n";<o:p></o:p></p><p class=MsoNormal>open ( my $output_file_handler , "> $encoding" , $output_filename_with_path ) or die "Could not open file $output_filename_with_path due to $! \r\n";<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># Bring the file contents in one line at a time<o:p></o:p></p><p class=MsoNormal># When you use <filehandler> syntax, the <> brackets bring in the next line of a file <o:p></o:p></p><p class=MsoNormal># When you us <STDIN>, the system assumes there will user input from STDIN and waits<o:p></o:p></p><p class=MsoNormal># If you do not define a variable for bringing the contents in, Perl uses the system variable $_ to hold each line<o:p></o:p></p><p class=MsoNormal># So if you use while ( <file_handler> ) without assigning it to a variable, the line will be in $_<o:p></o:p></p><p class=MsoNormal># I prefer the explicit assignment to a variable so I use while ( my $variable = <file_handler> ) and use $variable<o:p></o:p></p><p class=MsoNormal># This script is parsing a specific format to convert it to another specific format, <o:p></o:p></p><p class=MsoNormal># so the sequence may not be exactly what you will want to use in your situation<o:p></o:p></p><p class=MsoNormal># but the functions shown give you a sample of what text manipulation looks like in Perl<o:p></o:p></p><p class=MsoNormal>#<o:p></o:p></p><p class=MsoNormal># Re-set the file pointer for the input file to the start of the file<o:p></o:p></p><p class=MsoNormal># Usually not needed but a good habit to get into<o:p></o:p></p><p class=MsoNormal>seek $raw_content_handler , 0 , 0;<o:p></o:p></p><p class=MsoNormal># Since we are creating a linux file from a Windows script, <o:p></o:p></p><p class=MsoNormal># the newline character will be Windows and will be problematic on linux<o:p></o:p></p><p class=MsoNormal># We need to change the $INPUT_RECORD_SEPARATOR = $/ but do not want to do that globally, just in this script<o:p></o:p></p><p class=MsoNormal># so we put the editing functions into a subroutine and change a local version of $/<o:p></o:p></p><p class=MsoNormal># Initialize the first line of the file (specific to this situation; normally initialize to blank)<o:p></o:p></p><p class=MsoNormal>our $edited_contents = "[virt-viewer]\n";<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>while ( my $file_line = <$raw_content_handler> ) {<o:p></o:p></p><p class=MsoNormal>               # Delete / skip lines that do not have space as the second character; they are delimeter lines<o:p></o:p></p><p class=MsoNormal>               if ( substr( $file_line , 1 , 1 ) ne ' ' ) {<o:p></o:p></p><p class=MsoNormal>                              next;<o:p></o:p></p><p class=MsoNormal>               # Delete / skip the header line<o:p></o:p></p><p class=MsoNormal>               } elsif ( substr( $file_line , 2 , 3 ) eq 'key' ) {<o:p></o:p></p><p class=MsoNormal>                              next;<o:p></o:p></p><p class=MsoNormal>               } else {<o:p></o:p></p><p class=MsoNormal>                              # Remove the Windows newline<o:p></o:p></p><p class=MsoNormal>                              chomp $file_line;<o:p></o:p></p><p class=MsoNormal>                              # The remaining lines have the content we want, so we will manipulate them to our format<o:p></o:p></p><p class=MsoNormal>                              # Delete the first two characters of each line (they are "| ")<o:p></o:p></p><p class=MsoNormal>                              substr( $file_line , 0 , 2 ) = '';<o:p></o:p></p><p class=MsoNormal>                              # Convert what was the middle | character (now the first | in the line) to an = sign<o:p></o:p></p><p class=MsoNormal>                              # The raw syntax also has a space after the middle | so include it in the substitution<o:p></o:p></p><p class=MsoNormal>                              # and insert a ~ character before and after the = sign so we can hold a spot for them later<o:p></o:p></p><p class=MsoNormal>                              $file_line =~ s/\| /~=~/;<o:p></o:p></p><p class=MsoNormal>                              # Convert all the remaining | to spaces<o:p></o:p></p><p class=MsoNormal>                              $file_line =~ s/\|/ /g;<o:p></o:p></p><p class=MsoNormal>                              # Trim white space at start and end of each line<o:p></o:p></p><p class=MsoNormal>                              # Remove white spaces from left side of string<o:p></o:p></p><p class=MsoNormal>                              $file_line =~ s/^\s+//;<o:p></o:p></p><p class=MsoNormal>                              # Remove white spaces from right side of string<o:p></o:p></p><p class=MsoNormal>                              $file_line =~ s/\s+$//;<o:p></o:p></p><p class=MsoNormal>                              # Remove white space between first "word" and = sign<o:p></o:p></p><p class=MsoNormal>                              # Make sure there is at least one space before making the edit<o:p></o:p></p><p class=MsoNormal>                              my $first_space_location = index( $file_line , " " , 0 );<o:p></o:p></p><p class=MsoNormal>                              if ( $first_space_location != -1 ) {<o:p></o:p></p><p class=MsoNormal>                                             my $first_character_location_after_space = index($file_line , "~" , $first_space_location + 1 );<o:p></o:p></p><p class=MsoNormal>                                             substr( $file_line , $first_space_location , $first_character_location_after_space - $first_space_location ) = '';<o:p></o:p></p><p class=MsoNormal>                              }<o:p></o:p></p><p class=MsoNormal>                              # Delete the temporary placeholder ~ characters surrounding the = sign<o:p></o:p></p><p class=MsoNormal>                              $file_line =~ s/\~//g;<o:p></o:p></p><p class=MsoNormal>                              # Save the remaining characters into the output line with the linux newline<o:p></o:p></p><p class=MsoNormal>                              $edited_contents .= $file_line . "\n";<o:p></o:p></p><p class=MsoNormal>               }<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># For debugging if running the script from a Windows command line<o:p></o:p></p><p class=MsoNormal># print "The ticket created will contain \r\n";<o:p></o:p></p><p class=MsoNormal># print $edited_contents;<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># Save the edited contents into the output file<o:p></o:p></p><p class=MsoNormal># Set the file pointer to the start of the file and erase the existing contents to create a blank file<o:p></o:p></p><p class=MsoNormal># Not necessary for a new file but a good habit to get into IF you do not want to append to a file's contents<o:p></o:p></p><p class=MsoNormal>seek $output_file_handler , 0 , 0;<o:p></o:p></p><p class=MsoNormal>truncate $output_file_handler , 0;<o:p></o:p></p><p class=MsoNormal># Write your edited contents to the output file<o:p></o:p></p><p class=MsoNormal>print $output_file_handler $edited_contents;<o:p></o:p></p><p class=MsoNormal># Close the file - which will ensure that buffered content is completely written and any lock is released<o:p></o:p></p><p class=MsoNormal>close $raw_content_handler or die "Could not close the raw content holder due to $! so content may not be properly written \r\n";<o:p></o:p></p><p class=MsoNormal>close $output_file_handler or die "Could not close the file $output_filename_with_path due to $! so content may not be properly written \r\n";<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># Launch the remote-viewer application using the formatted configuration file <o:p></o:p></p><p class=MsoNormal># There will be a series of warnings generated but the viewer will launch and work<o:p></o:p></p><p class=MsoNormal>system ( "$path_to_remoteviewer_program" , "$output_filename_with_path" ) or die "Errors encountered while launching from $path_to_remoteviewer_program \r\n";<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal># For debugging if running the script from a Windows command line<o:p></o:p></p><p class=MsoNormal># Show that you made it to the end of the script<o:p></o:p></p><p class=MsoNormal># print "\r\n\r\nMade it to the end of the script!\r\n\r\n";<o:p></o:p></p></div></body></html>