Amith George
Amith George

Reputation: 5916

Stream output from external command from inside a powershell function

Consider the following command:

7z.exe a -t7z folder.7z folder

I have the following two stripped down powershell scripts

File 1: common.ps1

function Archive-Folder ($src, $dest_path, $archive_name) {

    $script_dir = split-path $script:MyInvocation.MyCommand.Path

    if ((test-path $src) -eq $false) {
        write-error "$src is not a valid source directory"
        #return
        return $false
    }

    if ((test-path $dest_path) -eq $false) {
        write-error "$dest_path is not a valid destination directory"
        #return
        return $false
    }

    if ([string]::IsNullOrWhiteSpace($archive_name) -eq $true) {
        write-error "$archive_name is not a valid archive name"
        #return
        return $false
    }

    write-verbose "archiving the folder"

    $archive_command = "$script_dir\7z.exe a -t7z $dest_path\$archive_name $src"

    $exe = "$script_dir\7z.exe"
    $arguments = @('a', '-t7z', "$dest_path\$archive_name", "$src")

    iex $archive_command

    # this doesn't stream the output. it prints it all at once.
    # & $exe $arguments | write-verbose

    return $true
}

File 2: script.ps1

$script_dir = split-path $script:MyInvocation.MyCommand.Path
. "$script_dir\common.ps1"

$VerbosePreference = "Continue"

$src = 'C:\some\source'
$backup_path = 'C:\some\destination'
$date_format = 'yyyy_MM_dd_HHmm'
$date = get-date
$date_str = $date.tostring($date_format)
$date_ticks = $date.ticks
$archive_name = "backup-$date_str-$date_ticks.7z"

# this prints the output streamed. The output ends with `True`
archive-folder $src $backup_path $archive_name 

# the following however doesn't output anything. in order to separate the command output from my function output, 
# i was printing the command output using write-verbose
$isSuccess = archive-folder $src $backup_path $archive_name
if ($isSuccess -eq $true) {
    #proceed with the rest of the code
}

With inputs from @Christian & @zdan, I was able to isolate the issue to the capturing of the return value. Similar to archive-folder, I have other functions that execute some commandline tool. I was thinking that each of these functions can return a true or false depending on whether the function was called with the right operations and the commandline tool executed properly.

However, if I capture the return value of my archive-folder function, then the output of the command doesn't get printed to the console. Also, my return value doesn't consist of a true or false value. It consists of the entire output of the command.

My first attempt at solving this was to write the command execution statement as iex $archive_command | write-verbose, but this did not stream the output.

I suppose I can check for side effects the commandline tool has in case of success (like presence of the archive file) to determine whether my function executed successfully, but am not sure if I will be able to do this for all functions that I may end up creating.

Is there a way to return a value and also stream the output of a commandline tool?

EDIT 2

With regards to why am I diving the code into two separate files/functions, my actual use scenario is as follows

The script.ps1 will be coordinating this flow. Backup the database (mongodb generates files for each collection of the db). Archive the database backup. Upload the archive to S3. Each of these steps will be done by a separate function in common.ps1. The script.ps1 will only contain the glue code. Posting all this might have complicated the question and I felt wasn't needed to understand the issue am facing

EDIT 1

If the folder being compressed has 5 files, 7zip will first output the copyright. Then it will output the text Scanning. Then it will output a line Creating archive at some location. Then it will process each file, outputting the progress for each file, one by one. This way, we get constant feedback about the progress of the operation.

If I execute the powershell function, then I see no output for the duration of the operation and then all the output at once. I get no feedback from 7zip. I would like to simulate the behaviour that 7zip shows when ran as a standalone exe.

Upvotes: 1

Views: 5411

Answers (3)

W1M0R
W1M0R

Reputation: 3626

This works for me:

& 7z.exe a -t7z -bsp1 $archive_name $src 2>&1 | Out-Host

The -bsp1 switch redirects the progress information stream to the stdout stream. This is a feature of 7z. Also look at the -bb switch.

The 2>&1 redirects the error stream to stdout. This is a feature of PowerShell.

-bs (Set output stream for output/error/progress line) switch Syntax

Syntax

-bs{o|e|p}{0|1|2}

{id} | Stream Type
................................
 o   | standard output messages
 e   | error messages
 p   | progress information
{N} | Stream Destination
................................
 0   | disable stream
 1   | redirect to stdout stream
 2   | redirect to stderr stream

Upvotes: 6

CB.
CB.

Reputation: 60976

why not this way? If you need

function archive-folder ($src, $archive_name) {   
$origcolor = [console]::ForegroundColor
[console]::ForegroundColor = "yellow"
"archiving $src"
$command = "7z.exe a -t7z $archive_name $src"
iex $command    
[console]::ForegroundColor = $origcolor    
}

My trivial caller function:

Function k
{
  dir c:\ps\ita
  Write-Host "Starting 7zippping from K function"
  archive-folder -archive_name c:\ps\pippo.7z c:\ps\ita
  Write-Host "7zipping from function k ended"
}

and what I see in powershell console ( in yellow the output from archive-folder)

k

    Directory: C:\ps\ita


Mode           LastWriteTime       Length Name
----           -------------       ------ ----
-a---    08/06/2011    19:26     5,502 MB ita.txt
-a---    28/05/1994    16:59   165,624 KB ITALIANO.A
-a---    28/05/1994    16:54    53,903 KB ITALIANO.B
-a---    28/05/1994    17:00   165,541 KB ITALIANO.C
-a---    08/06/2011    11:06    98,609 KB ITALIANO.D
-a---    28/05/1994    17:00    72,077 KB ITALIANO.E
-a---    28/05/1994    16:54    80,813 KB ITALIANO.F
-a---    28/05/1994    16:55    78,312 KB ITALIANO.G
-a---    28/05/1994    16:55     2,412 KB ITALIANO.H
-a---    08/06/2011    11:07   298,609 KB ITALIANO.I
-a---    28/05/1994    16:55     1,033 KB ITALIANO.J
-a---    28/05/1994    16:55     1,777 KB ITALIANO.K
-a---    28/05/1994    17:01    71,553 KB ITALIANO.L
-a---    08/06/2011    10:59   162,084 KB ITALIANO.M
-a---    28/05/1994    16:56    47,123 KB ITALIANO.N
-a---    28/05/1994    16:56    72,973 KB ITALIANO.O
-a---    08/06/2011    19:37   264,109 KB ITALIANO.P
-a---    28/05/1994    16:56    10,512 KB ITALIANO.Q
-a---    08/06/2011    19:38   327,348 KB ITALIANO.R
-a---    08/06/2011    19:40   566,512 KB ITALIANO.S
-a---    08/06/2011    10:57   184,719 KB ITALIANO.T
-a---    28/05/1994    16:57    19,378 KB ITALIANO.U
-a---    28/05/1994    16:57    61,552 KB ITALIANO.V
-a---    28/05/1994    16:57     1,334 KB ITALIANO.W
-a---    28/05/1994    16:57     1,368 KB ITALIANO.X
-a---    28/05/1994    16:57       533 B  ITALIANO.Y
-a---    28/05/1994    17:01     7,054 KB ITALIANO.Z
Starting 7zippping from K funztion
archiving c:\ps\ita

7-Zip 4.65  Copyright (c) 1999-2009 Igor Pavlov  2009-02-03

Scanning

Updating archive c:\ps\pippo.7z

Compressing  ITA\ITALIANO.H
Compressing  ITA\ITALIANO.C
Compressing  ITA\ITALIANO.F
Compressing  ITA\ita.txt
Compressing  ITA\ITALIANO.O
Compressing  ITA\ITALIANO.A
Compressing  ITA\ITALIANO.B
Compressing  ITA\ITALIANO.D
Compressing  ITA\ITALIANO.E
Compressing  ITA\ITALIANO.G
Compressing  ITA\ITALIANO.I
Compressing  ITA\ITALIANO.J
Compressing  ITA\ITALIANO.K
Compressing  ITA\ITALIANO.L
Compressing  ITA\ITALIANO.M
Compressing  ITA\ITALIANO.N
Compressing  ITA\ITALIANO.P
Compressing  ITA\ITALIANO.Q
Compressing  ITA\ITALIANO.R
Compressing  ITA\ITALIANO.S
Compressing  ITA\ITALIANO.T
Compressing  ITA\ITALIANO.U
Compressing  ITA\ITALIANO.V
Compressing  ITA\ITALIANO.W
Compressing  ITA\ITALIANO.X
Compressing  ITA\ITALIANO.Y
Compressing  ITA\ITALIANO.Z

Everything is Ok
7zipping from function k ended

Upvotes: 0

zdan
zdan

Reputation: 29470

It seems to me that you should be able to just do:

&7z.exe a -t7z $archive_name $src | write-verbose

Unless there is something I am missing.

Upvotes: 0

Related Questions