Замена строки в нескольких файлах с их именем - PullRequest
0 голосов
/ 30 сентября 2018

Итак, у меня есть целая большая папка с текстовыми файлами, в которой я хотел бы изменить строку unamed в каждом из них на имя самого файла, исключая расширение.

Пока что с моими навыками мусора я придумал следующее, но это не дает того, что я ищу, так как оно меняет имя на каждый собранный файл

$path = "*.txt"
$filename = Get-Item $path | Select-Object -ExpandProperty BaseName
$Change = Get-Content $path
$Change | ForEach-Object {$_ -Replace "unamed", $filename} | Set-Content $path

Так что в основном что-то вродеthis

Имя этого файла не указано.

становится

Имя этого файла: File1Name File2Name File3Name.

Существует также побочная проблема, с которой я сталкиваюсь, когда результаты всех файлов объединяются в один файл (поэтому содержимое файла 1 теперь будет также содержать содержимое всех других файлов).Как я могу получить результаты, которые я ищу, о, мудрые?

Если есть способ уладить это с помощью обычной командной строки вместо powershell, то я согласен и с этим (я нашел это, но имел проблему с replace_stringнедопустимая команда? Пакетный скрипт, который заменяет статическую строку в файле именем файла )

Ответы [ 2 ]

0 голосов
/ 30 сентября 2018

Ответ эсперанто хорошо работает и PSv2-совместим;вот гораздо более быстрое - но более интенсивное использование памяти - решение PSv3 +:

Get-Content -Raw *.txt | ForEach-Object {
  $_ -replace 'unamed', [IO.Path]::GetFileNameWithoutExtension($_.PSPath) |
    Set-Content $_.PSPath
}
  • Если у вас нет особых требований к сопоставлению файлов, таких как поиск файлов рекурсивно иливам нужно исключить файлы или сопоставить по атрибутам файлов, вам не нужно Get-Item / Get-ChildItem для перечисления интересующих файлов - вы можете передать шаблон подстановки, например *.txt, непосредственно Get-Content (подразумевается)) -Path параметр.

  • Get-Content -Raw (PSv3 +) считывает все содержимое каждого файла, соответствующего *.txt, в память в целом как одна строка .

    • Примечание. Поскольку каждый файл читается целиком, прежде чем он выводится в конвейер, это решение может не работать с очень большими файлами, в зависимости от доступной памяти.
  • Get-Content украшает строки, которые он выводит, информацией о том, откуда взялась строка, так что свойство .PSPath в каждой возвращаемой строке содержит полное имя файлафайл строка была повторноad from.

  • [IO.Path]::GetFileNameWithoutExtension($_.PSPath) извлекает корень имени файла из пути (простое имя файла без расширения)

    • Необходимо прямое использование .NET,к сожалению, командлет Split-Path не позволяет вам сделать это в Windows PowerShell, но в PowerShell Core вместо этого можно использовать Split-Path -LeafBase.

Caveat : Set-Content использует кодировку по умолчанию (Windows PowerShell: "ANSI";PowerShell Core : UTF-8 без спецификации), который может совпадать или не совпадать с кодировкой ввода;при необходимости используйте параметр -Encoding.


Что касается того, что вы пытались :

$filename = Get-Item $path | Select-Object -ExpandProperty BaseName

$filename теперь содержит массив корней имен файлов (базовых имен).

$Change = Get-Content $path

$Change теперь содержит отдельный массив , элементами которого являются строки из всех входных файлов .

$Change | ForEach-Object {$_ -Replace "unamed", $filename} | Set-Content $path

  • Поскольку $Change представляет собой один плоский массив, вы потеряли разделение элементов массива на файлы или источник, а Set-Content отправляет все строк в целевой файл (ы).

  • Поскольку $filename - это массив , тогда как 2-й операнд RHS -replace поддерживает только скаляр (одна строка), $_ -replace "unamed", $filenameприводит к преобразованию $filename в одну строку, которая представляет собой разделенную пробелами конкатенацию элементов массива (корни файлов).

  • Set-Content $Path такслучается, записать плоский (модифицированный) массив строк в мюltiple файлов, а именно те существующие файлы, которые соответствуют подстановочному выражению в $Path, *.txt:

    • Как указано, входные данные Set-Content, полученные как объединение строк входных файлов, поэтому все выходные файлы получают объединенное (измененное) содержимое из всех входных файлов (все файлы, имена которых соответствуют шаблону с подстановочными знаками *.txt)

    • В качестве отступления: Set-Content *.txt позволяет записывать одинаковое содержимое в несколько ранее существующих выходных файлов - удивительная и, возможно, деструктивная функция, которую, вероятно, не следует поддерживать -см. это обсуждение на GitHub .

0 голосов
/ 30 сентября 2018

Как то так?

Get-ChildItem -Filter "*.txt" | ForEach-Object {
  $Path=$_.FullName; (Get-Content $Path) -replace 'unamed', $_.BaseName | Set-Content $Path 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...