Jak wyprowadzić tekst bez nowej linii w programie PowerShell?


145

Chcę, aby mój skrypt PowerShell wydrukował coś takiego:

Enabling feature XYZ......Done

Skrypt wygląda mniej więcej tak:

Write-Output "Enabling feature XYZ......."
Enable-SPFeature...
Write-Output "Done"

Ale Write-Outputzawsze wypisuje nowy wiersz na końcu, więc mój wynik nie znajduje się w jednym wierszu. Czy jest na to sposób?


3
Możliwy duplikat unikania
znaku

Odpowiedzi:


165

Write-Host -NoNewline "Włączanie funkcji XYZ ......."


62
Głosowanie negatywne, ponieważ przykład PO konkretnie używa Write-Output, który ma znacznie inną funkcję niż Write-Host. Czytelnicy powinni zwrócić uwagę na tę dużą rozbieżność przed skopiowaniem / wklejeniem odpowiedzi.
NathanAldenSr

5
Zgadzam się z @NathanAldenSr, Write-Host nie pomaga, jeśli próbujesz wyprowadzić do pliku itp.
stevethethread

6
Write-Hostprawie nigdy nie jest właściwą odpowiedzią. To odpowiednik pracy >/dev/ttyw Unixlandzie.
Mark Reed

2
Write-Progressmoże być odpowiednie w niektórych przypadkach, patrz przykład poniżej.
Thomas

12
Głosowanie negatywne, ponieważ przykład PO konkretnie używa Write-Output. Write-Outputnie ma -NoNewLineparametru.
Slogmeister Extraordinaire

49

Niestety, jak zauważono w kilku odpowiedziach i komentarzach, Write-Hostmoże być niebezpieczny i nie można go przesłać potokiem do innych procesów i Write-Outputnie ma -NoNewlineflagi.

Jednak metody te są „* nix” Sposoby progresji wyświetlacza, sposób „PowerShell” do zrobienia, że wydaje się być Write-Progress: wyświetla pasek w górnej części okna PowerShell z informacjami o postępach, dostępnego od PowerShell 3.0 dalszej, patrz instrukcja dla Detale.

# Total time to sleep
$start_sleep = 120

# Time to sleep between each notification
$sleep_iteration = 30

Write-Output ( "Sleeping {0} seconds ... " -f ($start_sleep) )
for ($i=1 ; $i -le ([int]$start_sleep/$sleep_iteration) ; $i++) {
    Start-Sleep -Seconds $sleep_iteration
    Write-Progress -CurrentOperation ("Sleep {0}s" -f ($start_sleep)) ( " {0}s ..." -f ($i*$sleep_iteration) )
}
Write-Progress -CurrentOperation ("Sleep {0}s" -f ($start_sleep)) -Completed "Done waiting for X to finish"

I weźmy przykład PO:

# For the file log
Write-Output "Enabling feature XYZ"

# For the operator
Write-Progress -CurrentOperation "EnablingFeatureXYZ" ( "Enabling feature XYZ ... " )

Enable-SPFeature...

# For the operator
Write-Progress -CurrentOperation "EnablingFeatureXYZ" ( "Enabling feature XYZ ... Done" )

# For the log file
Write-Output "Feature XYZ enabled"

3
Myślę, że to najlepsze rozwiązanie do pokazywania statusu. Jeśli potrzebujesz dziennika lub czegoś innego, musisz żyć z przełamaniem linii w trybie Write-Output.
fadanner

1
Zgoda, a celem wyświetlania progresywnego jest po prostu „mieć ochotę” podczas instalacji na żywo, nie ma sensu umieszczać tego w plikach dziennika: drukuj „zacznij coś robić”, a następnie „zrób coś”
Thomas

13

Chociaż może to nie działać w twoim przypadku (ponieważ dostarczasz użytkownikowi informacje wyjściowe), utwórz ciąg, którego możesz użyć do dołączenia wyniku. Kiedy nadejdzie czas, aby go wyprowadzić, po prostu wyślij ciąg.

Ignorując oczywiście, że ten przykład jest głupi w twoim przypadku, ale przydatny w koncepcji:

$output = "Enabling feature XYZ......."
Enable-SPFeature...
$output += "Done"
Write-Output $output

Wyświetlacze:

Enabling feature XYZ.......Done

1
Może to zadziałać w konkretnym podanym przykładzie, ale nadal istnieje dodatkowy wysuw wiersza utworzony przez Write-Output. Rozsądne obejście, ale nie rozwiązanie.
Slogmeister Extraordinaire

Write-Output zawsze wyświetla na końcu znak nowej linii. Nie
da się

7
Nie o to chodzi, ponieważ całe dane wyjściowe pojawiają się po zainstalowaniu funkcji.
majkinetor

10
Nie rozumiem, kto daje więcej niż 1 upwote na to pytanie, ponieważ nie
maxkoryukov

1
„Ignorując oczywiście, że ten przykład jest głupi w twoim przypadku, ale przydatny w koncepcji:”
shufler,

6

Tak, ponieważ inne odpowiedzi mają stany, nie można tego zrobić za pomocą Write-Output. Tam, gdzie PowerShell nie działa, zwróć się do .NET, jest tu nawet kilka odpowiedzi dotyczących .NET, ale są one bardziej złożone niż powinny.

Po prostu użyj:

[Console]::Write("Enabling feature XYZ.......")
Enable-SPFeature...
Write-Output "Done"

Nie jest to najczystszy PowerShell, ale działa.


5
Negocjowany, ponieważ zachowuje się tak samo Write-Host, ale ludzie się tego nie spodziewają.
JBert

4

Aby zapisać do pliku, możesz użyć tablicy bajtów. Poniższy przykład tworzy pusty plik ZIP, do którego można dodawać pliki:

[Byte[]] $zipHeader = 80, 75, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
[System.IO.File]::WriteAllBytes("C:\My.zip", $zipHeader)

Albo użyj:

[Byte[]] $text = [System.Text.Encoding]::UTF8.getBytes("Enabling feature XYZ.......")
[System.IO.File]::WriteAllBytes("C:\My.zip", $text)

To wspaniały przykład!
Peter Mortensen

2

Uproszczenie odpowiedzi FrinkTheBrave:

[System.IO.File]::WriteAllText("c:\temp\myFile.txt", $myContent)

To wcale nie odpowiada na pytanie.
NathanAldenSr

2
Ale właśnie tego szukałem i tego się spodziewałem po tytule pytania.
Patrick Roocks

2

Problem, który napotkałem, polegał na tym, że Write-Output faktycznie przerywa linię wyjściową podczas korzystania z PowerShell v2, przynajmniej na stdout. Próbowałem napisać tekst XML na standardowe wyjście bez powodzenia, ponieważ byłby mocno zawinięty w znak 80.

Obejście polegało na użyciu

[Console]::Out.Write($myVeryLongXMLTextBlobLine)

To nie był problem w PowerShell v3. Wydaje się, że funkcja Write-Output działa tam poprawnie.

W zależności od sposobu wywoływania skryptu programu PowerShell może być konieczne użycie

[Console]::BufferWidth =< length of string, e.g. 10000)

zanim napiszesz na standardowe wyjście.


2
Zachowuje się jak Write-Host, ale gorzej. Nie możesz na przykład przesłać go do pliku.
majkinetor

2

Wydaje się, że nie ma sposobu, aby to zrobić w programie PowerShell. Wszystkie poprzednie odpowiedzi są niepoprawne, ponieważ nie zachowują się tak, jak się Write-Outputzachowują, a raczej takie, Write-Hostktóre i tak nie mają tego problemu.

Rozwiązanie close wydaje się używać Write-Hostz -NoNewLineparametrem. Nie można tego potokować, co jest ogólnie problemem, ale istnieje sposób na nadpisanie tej funkcji, jak opisano w Write-Host => Export do pliku , dzięki czemu można łatwo sprawić, by zaakceptował parametr dla pliku wyjściowego. To wciąż nie jest dobre rozwiązanie. Dzięki Start-Transcripttemu jest bardziej użyteczny, ale to polecenie cmdlet ma problemy z aplikacjami natywnymi.

Write-Outputpo prostu nie możesz zrobić tego, czego potrzebujesz w ogólnym kontekście.


2

Write-Hostjest okropny, niszczyciel światów, ale możesz go używać tylko do wyświetlania postępu użytkownikowi podczas korzystania Write-Outputz logowania (nie to, że OP prosił o logowanie).

Write-Output "Enabling feature XYZ" | Out-File "log.txt" # Pipe to log file
Write-Host -NoNewLine "Enabling feature XYZ......."
$result = Enable-SPFeature
$result | Out-File "log.txt"
# You could try{}catch{} an exception on Enable-SPFeature depending on what it's doing
if ($result -ne $null) {
    Write-Host "complete"
} else {
    Write-Host "failed"
}

W zależności od twoich potrzeb w zakresie wskazywania postępów, jest też Write-Progress.
chwarr

1

Po prostu nie można zmusić PowerShell do pominięcia tych nieznośnych znaków nowej linii ... Nie ma takiego skryptu ani polecenia cmdlet. Oczywiście, Write-Host to absolutny nonsens, ponieważ nie można z niego przekierować / potokować!

Niemniej jednak możesz napisać własny plik EXE, aby to zrobić, co wyjaśniłem w pytaniu o przepełnienie stosu Jak wyprowadzić coś w PowerShell .


2
Błędne informacje. Jak świetnie odpowiedzieli Shay i Jay, po prostu dodaj -NoNewline jako pierwszy argument.
David w HotspotOffice

2
Może tak jest teraz @DavidatHotspotOffice, ale kiedy ostatnio dotknąłem okna systemu Windows (ponad rok temu), które nie działało, nie można było przekierować / potok z Write-Host. Szczerze mówiąc, nie miałem najmniejszej cierpliwości do POSH czy .NET, zrezygnowałem po kilku miesiącach i wróciłem do unixa. śmieszne
samthebest

3
@DavidatHotspotOffice - Właściwie ma rację. Nie ma argumentu „NoNewLine” dla Write-Output, o co pytano w pierwotnym pytaniu. Wydaje się, że istnieje kilka dobrych powodów, dla których warto używać funkcji Write-Output - więc ta odpowiedź ma sens. jsnover.com/blog/2013/12/07/write-host-considered-harmful
James Ruskin

Ocena negatywna, ponieważ pytanie dotyczy rozwiązania „w PowerShell”. Pisanie zewnętrznego pliku EXE nie odbywa się w programie PowerShell.
Slogmeister Extraordinaire

1
@SlogmeisterExtraordinaire W PowerShell nie jest to możliwe, dlatego moja odpowiedź jest rozsądna. Po prostu odrzucasz głos, ponieważ jesteś tak smutny, że musisz pracować z najgorszym systemem operacyjnym na świecie, który ma najgorszą powłokę na świecie.
samthebest

1

Odpowiedź Shuflera jest poprawna. Podane w inny sposób: Zamiast przekazywać wartości do Write-Output za pomocą ARRAY FORM,

Write-Output "Parameters are:" $Year $Month $Day

lub ekwiwalent przez wielokrotne wywołania Write-Output,

Write-Output "Parameters are:"
Write-Output $Year
Write-Output $Month
Write-Output $Day
Write-Output "Done."

najpierw połącz swoje komponenty w STRING VARIABLE:

$msg="Parameters are: $Year $Month $Day"
Write-Output $msg

Zapobiegnie to powstawaniu pośrednich list CRLF spowodowanych wielokrotnym wywołaniem Write-Output (lub ARRAY FORM), ale oczywiście nie pominie końcowego CRLF polecenia Write-Output. W tym celu będziesz musiał napisać własne polecenie, użyć jednego z innych zawiłych obejść wymienionych tutaj lub poczekać, aż Microsoft zdecyduje się obsługiwać -NoNewlineopcję zapisu-wyjścia.

Twoje pragnienie dostarczenia tekstowego miernika postępu do konsoli (tj. „…”) W przeciwieństwie do zapisywania do pliku dziennika, powinno być również zaspokojone przez użycie Write-Host. Możesz osiągnąć jedno i drugie, zbierając tekst msg do zmiennej w celu zapisania w dzienniku ORAZ używając Write-Host, aby zapewnić postęp konsoli. Funkcjonalność tę można połączyć we własne polecenie w celu maksymalnego ponownego wykorzystania kodu.


Wolę tę odpowiedź od innych. Jeśli wywołujesz właściwości obiektów, nie możesz ich ująć w cudzysłowy, więc użyłem: Write-Output ($ msg = $ MyObject.property + "Jakiś tekst, który chcę zawrzeć" + $ Object.property)
Lewis

2
@Lewis Z pewnością możesz zawrzeć właściwości obiektu wewnątrz ciągu! Użyj wyrażenia $ (), aby otoczyć dowolną zmienną, np. „$ ($ MyObject.Property) Jakiś tekst, który chcę uwzględnić $ ($ Object.property)”
shufler

Może to zadziałać w konkretnym podanym przykładzie, ale nadal istnieje dodatkowy wysuw wiersza Write-Output, po prostu nie możesz go zobaczyć, ponieważ jest to ostatnia rzecz zapisana. Rozsądne obejście, ale nie rozwiązanie. Może istnieć coś pochłaniającego wynikowe dane wyjściowe, co nie może obsłużyć końcowego znaku nowej linii.
Slogmeister Extraordinaire

1
Niepoprawne. Rozwiązania nie da się zrobić jednym poleceniem.
majkinetor

To nie odpowiada na pytanie. Dane wyjściowe pliku dziennika powinny pokazywać operację, która miała zostać podjęta, aby można było zobaczyć i prześledzić niepowodzenie. Konkatenacja tego nie osiąga.
durette

0

Poniższe instrukcje umieszczą kursor z powrotem na początku poprzedniego wiersza. Do Ciebie należy ustawienie go w odpowiedniej poziomej pozycji (używając $ pos.X, aby przesunąć go na boki):

$pos = $host.ui.RawUI.get_cursorPosition()
$pos.Y -= 1
$host.UI.RawUI.set_cursorPosition($Pos)

Twoje aktualne wyjście ma 27 spacji, więc $ pos.X = 27 może działać.


Nie ma to nic wspólnego z plikiem wyjściowym.
Slogmeister Extraordinaire

Nie jest też tak źle. Jeśli $pos.Xrównież to zrobisz, może dać prawidłowe wyniki . Problem polega na tym, że jeśli przepuścisz go do pliku, pojawią się dwie oddzielne linie.
majkinetor

0

Może nie jest strasznie elegancki, ale robi dokładnie to, o co prosił OP. Zauważ, że ISE miesza się ze StdOut, więc nie będzie wyjścia. Aby zobaczyć, jak działa ten skrypt, nie można go uruchomić w ISE.

$stdout=[System.Console]::OpenStandardOutput()
$strOutput="Enabling feature XYZ... "
$stdout.Write(([System.Text.Encoding]::ASCII.GetBytes($strOutput)),0,$strOutput.Length)
Enable-SPFeature...
$strOutput="Done"
$stdout.Write(([System.Text.Encoding]::ASCII.GetBytes($strOutput)),0,$strOutput.Length)
$stdout.Close()

1
Niepoprawne. Jeśli umieścisz to w pliku i potokujesz wyjście, nic się nie pojawi.
majkinetor

Pipowanie do pliku nie było czymś, o co prosił OP. I tak, [System.Console]nie można przekierować do pliku.
Slogmeister Extraordinaire

0

Oszukiwałem, ale uważam, że to jedyna odpowiedź, która spełnia wszystkie wymagania. Mianowicie, pozwala to uniknąć końcowego CRLF, zapewnia miejsce na zakończenie innej operacji w międzyczasie i odpowiednio przekierowuje na standardowe wyjście, jeśli jest to konieczne.

$c_sharp_source = @"
using System;
namespace StackOverflow
{
   public class ConsoleOut
   {
      public static void Main(string[] args)
      {
         Console.Write(args[0]);
      }
   }
}
"@
$compiler_parameters = New-Object System.CodeDom.Compiler.CompilerParameters
$compiler_parameters.GenerateExecutable = $true
$compiler_parameters.OutputAssembly = "consoleout.exe"
Add-Type -TypeDefinition $c_sharp_source -Language CSharp -CompilerParameters $compiler_parameters

.\consoleout.exe "Enabling feature XYZ......."
Write-Output 'Done.'

0
$host.UI.Write('Enabling feature XYZ.......')
Enable-SPFeature...
$host.UI.WriteLine('Done')

0

żądane o / p: Włączanie funkcji XYZ ...... Gotowe

możesz użyć poniższego polecenia

$ a = "Włączanie funkcji XYZ"

Wyjście do zapisu "$ a ...... Gotowe"

musisz dodać zmienną i instrukcję w cudzysłowie. mam nadzieję, że to jest pomocne :)

Dzięki Techiegal


Funkcja Write-Output jest preferowana do umieszczania obiektów w potoku. W przypadku wyświetlania tekstu często używany jest Write-Host, a ostatnio Write-Information służy do zapisywania w strumieniu informacji.
logicaldiagram

0

Jest tu już tyle odpowiedzi, że nikt tutaj nie przewinie. W każdym razie istnieje również rozwiązanie umożliwiające pisanie tekstu bez nowej linii na końcu, z następującymi elementami:

Plik wyjściowy z kodowaniem:

  # a simple one liner
  "Hello World, in one line" | Out-File -Append -Encoding ascii -NoNewline -FilePath my_file.txt;

  # this is a test to show how it works
  "Hello World".Split(" ") | % { "_ $_ _" | Out-File -Append -Encoding ascii -NoNewline -FilePath my_file.txt; }

Wyjście Consol:

  # a simple one liner
  "Hello World, in one line" | Write-Host -NoNewline;

  # this is a test to show how it works
  "Hello World".Split(" ") | % { "_ $_ _" | Write-Host -NoNewline; }

-3

Możesz to zrobić absolutnie. Write-Output ma flagę o nazwie „ NoEnumerate ”, która jest zasadniczo tym samym.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.