При использовании Get-Content
для удаления пробелов из файла:
$file = "C:\Folder\MyFile001.txt"
Get-Content -Path $file | ? { -not [string]::IsNullOrWhiteSpace($_) } | Set-Content -Path $file
Показывает сообщение об ошибке «Set-Content: процесс не может получить доступ к файлу»…, поскольку он используется другим процессом.
Исправление заключается в использовании скобок с Get-Content
:
(Get-Content -Path $file) | ...
Что является причиной этого? Я хотел бы понять, какую разницу имеют круглые скобки и относится ли это к другим командлетам.
Скобки работают в Powershell так же, как и в алгебре.
Что-то вроде.
PowerShell сначала выполняет команду в круглых скобках, а затем пытается сопоставить ее тип с тем, куда она идет. В этом случае вы говорите ему завершить выполнение Get-Content и чтение буфера в память, прежде чем он перейдет к следующей команде, а не передавать все через конвейер.
Это похоже на то, как работает BaSH, где вы используете что-то вроде (при условии, что «service.txt» содержит одну строку, которая говорит apache):
Service $(cat /etc/service.txt) Stop
>> Service apache stop
Который выполнит команду в $() и вернет ее в виде текста перед выполнением остальной части команды.
В PowerShell вы можете использовать возможность сопоставления типов скобок, чтобы делать изящные вещи, например возвращать только одно свойство объекта:
(Get-ChildItem -filter "*.pdf" -Path .\Projects).FullName
>> C:\Users\ncf\Projects\Documentation.pdf
Когда командлет выводит что-то в конвейер, конвейер выполняется до обработки следующего вывода командлета. Итак, пока ваша строка движется вниз по конвейеру к Set-Content
, Get-Content
все еще активен (с открытым выходным файлом), ожидая своей очереди для продолжения выполнения. Однако, когда вы заключаете выражение в ()
, весь вывод собирается в массив, который затем передается по конвейеру. Get-Content
сделано и файл закрыт. Для демонстрации рассмотрим эту функцию:
function get-10dates { $count = 0; while($count -lt 10){ get-date; $count += 1 }}
когда вы запускаете его и передаете его в цикл foreach
:
get-10dates | foreach { $_; sleep -Seconds 1 }
вы получаете следующий вывод, где каждая дата задерживается на секунду из-за sleep
в foreach:
# ~> get-10dates | foreach { $_; sleep -Seconds 1 }
June 19, 2019 9:58:36 AM
June 19, 2019 9:58:37 AM
June 19, 2019 9:58:38 AM
June 19, 2019 9:58:39 AM
June 19, 2019 9:58:40 AM
June 19, 2019 9:58:41 AM
June 19, 2019 9:58:42 AM
June 19, 2019 9:58:43 AM
June 19, 2019 9:58:44 AM
June 19, 2019 9:58:45 AM
Теперь, если вы оберните функцию в ()
. вы можете видеть, что все 10 дат создаются одновременно (в течение 1 секунды), прежде чем они будут переданы по конвейеру:
# ~> (get-10dates) | foreach { $_; sleep -Seconds 1 }
June 19, 2019 9:58:53 AM
June 19, 2019 9:58:53 AM
June 19, 2019 9:58:53 AM
June 19, 2019 9:58:53 AM
June 19, 2019 9:58:53 AM
June 19, 2019 9:58:53 AM
June 19, 2019 9:58:53 AM
June 19, 2019 9:58:53 AM
June 19, 2019 9:58:53 AM
June 19, 2019 9:58:53 AM