ใช้ PowerShell ผมต้องการที่จะแทนที่เกิดขึ้นแน่นอนทั้งหมดในแฟ้มที่กำหนดด้วย[MYID]
MyValue
วิธีที่ง่ายที่สุดในการทำเช่นนั้นคืออะไร?
ใช้ PowerShell ผมต้องการที่จะแทนที่เกิดขึ้นแน่นอนทั้งหมดในแฟ้มที่กำหนดด้วย[MYID]
MyValue
วิธีที่ง่ายที่สุดในการทำเช่นนั้นคืออะไร?
คำตอบ:
ใช้ (รุ่น V3):
(Get-Content c:\temp\test.txt).replace('[MYID]', 'MyValue') | Set-Content c:\temp\test.txt
หรือสำหรับ V2:
(Get-Content c:\temp\test.txt) -replace '\[MYID\]', 'MyValue' | Set-Content c:\temp\test.txt
(Get-Content file.txt) |
Foreach-Object {$_ -replace '\[MYID\]','MyValue'} |
Out-File file.txt
โปรดทราบว่า(Get-Content file.txt)
ต้องใช้วงเล็บใน:
โดยไม่ต้องใส่วงเล็บเนื้อหาจะอ่านครั้งละหนึ่งบรรทัดและไหลไปป์ไลน์จนกว่าจะถึงไฟล์ที่ออกหรือชุดเนื้อหาซึ่งพยายามที่จะเขียนไปยังไฟล์เดียวกัน แต่มันเปิดแล้วโดยรับเนื้อหาและคุณจะได้รับ ข้อผิดพลาด วงเล็บทำให้การดำเนินการอ่านเนื้อหาทำได้ครั้งเดียว (เปิดอ่านและปิด) จากนั้นเมื่อทุกบรรทัดถูกอ่านบรรทัดเหล่านั้นจะถูกไพพ์ทีละครั้งและเมื่อถึงคำสั่งสุดท้ายในไพพ์ไลน์พวกเขาสามารถเขียนลงไฟล์ได้ มันเหมือนกับ $ content = content; $ เนื้อหา | ที่ ...
Set-Content
แทนที่จะเป็นOut-File
คุณได้รับคำเตือนเช่น"กระบวนการไม่สามารถเข้าถึงไฟล์ '123.csv' เพราะกระบวนการอื่นกำลังถูกใช้งาน" .
Get-Content
ในวงเล็บมันทำงาน คุณสามารถอธิบายในคำตอบของคุณได้ว่าเหตุใดจึงจำเป็นต้องใช้วงเล็บ ฉันจะแทนที่Out-File
ด้วยSet-Content
เพราะปลอดภัยกว่า มันปกป้องคุณจากการเช็ดไฟล์เป้าหมายถ้าคุณลืมวงเล็บ
Set-Content
แทนที่จะOut-File
เป็นวิธีที่ดีกว่าและปลอดภัยกว่ามาก ขออภัยต้องลงคะแนน
ฉันชอบใช้ File-class ของ. NET และวิธีการคงที่ตามที่เห็นในตัวอย่างต่อไปนี้
$content = [System.IO.File]::ReadAllText("c:\bla.txt").Replace("[MYID]","MyValue")
[System.IO.File]::WriteAllText("c:\bla.txt", $content)
นี้มีประโยชน์ในการทำงานกับสายอักขระตัวเดียวแทนที่จะเป็น String อาร์เรย์เช่นเดียวกับได้รับเนื้อหา วิธีการต่างๆยังดูแลการเข้ารหัสไฟล์ (UTF-8 BOMฯลฯ ) โดยที่คุณไม่ต้องเสียเวลาดูแลมากนัก
นอกจากนี้ยังมีวิธีการที่จะไม่ยุ่งขึ้นตอนจบบรรทัด (ตอนจบบรรทัด Unix ที่อาจจะใช้) ในทางตรงกันข้ามกับอัลกอริทึมที่ใช้รับเนื้อหาและท่อผ่านไปยังชุดเนื้อหา
ดังนั้นสำหรับฉัน: สิ่งที่น้อยลงที่สามารถทำลายในช่วงหลายปี
สิ่งที่ไม่ค่อยมีคนรู้จักเมื่อใช้คลาส. NET คือเมื่อคุณพิมพ์ใน "[System.IO.File] ::" ในหน้าต่าง PowerShell คุณสามารถกดTabปุ่มเพื่อทำตามวิธีการต่างๆ
[System.IO.File] | gm
C:\Windows\System32\WindowsPowerShell\v1.0
?
ไฟล์ด้านบนใช้สำหรับ "ไฟล์เดียว" เท่านั้น แต่คุณสามารถเรียกใช้ไฟล์นี้ได้หลายไฟล์ภายในโฟลเดอร์ของคุณ:
Get-ChildItem 'C:yourfile*.xml' -Recurse | ForEach {
(Get-Content $_ | ForEach { $_ -replace '[MYID]', 'MyValue' }) |
Set-Content $_
}
foreach
คุณสามารถทำได้Get-ChildItem 'C:\folder\file*.xml' -Recurse | ForEach { (Get-Content $_).Replace('[MYID]', 'MyValue') | Set-Content $_ }
foreach
เพราะ Get-Content ทำสิ่งที่คุณอาจไม่คาดคิด ... มันส่งคืนอาร์เรย์ของสตริงโดยที่แต่ละสตริงเป็นบรรทัดในไฟล์ หากคุณวนลูปผ่านไดเรกทอรี (และไดเรกทอรีย่อย) ที่อยู่ในตำแหน่งที่แตกต่างจากสคริปต์ที่ใช้งานของคุณคุณจะต้องการสิ่งนี้: Get-ChildItem $Directory -File -Recurse | ForEach { (Get-Content $_.FullName) | ForEach { $_ -replace '[MYID]', 'MyValue' } | Set-Content $_.FullName }
ที่ไหน$Directory
เป็นไดเรกทอรีที่มีไฟล์ที่คุณต้องการแก้ไข
คุณสามารถลองสิ่งนี้:
$path = "C:\testFile.txt"
$word = "searchword"
$replacement = "ReplacementText"
$text = get-content $path
$newText = $text -replace $word,$replacement
$newText > $path
นี่คือสิ่งที่ฉันใช้ แต่มันช้าในไฟล์ข้อความขนาดใหญ่
get-content $pathToFile | % { $_ -replace $stringToReplace, $replaceWith } | set-content $pathToFile
หากคุณกำลังจะถูกแทนที่สตริงในไฟล์ข้อความที่มีขนาดใหญ่และความเร็วเป็นกังวลมองในการใช้System.IO.StreamReaderและSystem.IO.StreamWriter
try
{
$reader = [System.IO.StreamReader] $pathToFile
$data = $reader.ReadToEnd()
$reader.close()
}
finally
{
if ($reader -ne $null)
{
$reader.dispose()
}
}
$data = $data -replace $stringToReplace, $replaceWith
try
{
$writer = [System.IO.StreamWriter] $pathToFile
$writer.write($data)
$writer.close()
}
finally
{
if ($writer -ne $null)
{
$writer.dispose()
}
}
(รหัสข้างต้นยังไม่ได้ทดสอบ)
อาจเป็นวิธีที่ยอดเยี่ยมกว่าในการใช้ StreamReader และ StreamWriter เพื่อแทนที่ข้อความในเอกสาร แต่ควรให้จุดเริ่มต้นที่ดีแก่คุณ
ผมพบว่าเล็ก ๆ น้อย ๆ ที่รู้จักกัน แต่วิธีที่เย็นที่น่าอัศจรรย์ใจที่จะทำมันได้จากเย็ตของWindows PowerShell ในการดำเนินการ คุณสามารถอ้างอิงไฟล์เช่นตัวแปรคล้ายกับ $ env: path แต่คุณต้องเพิ่มเครื่องหมายปีกกา
${c:file.txt} = ${c:file.txt} -replace 'oldvalue','newvalue'
$myFile
?
$a = 'file.txt'; invoke-expression "`${c:$a} = `${c:$a} -replace 'oldvalue','newvalue'"
หากคุณต้องการแทนที่สตริงในหลายไฟล์:
ควรสังเกตว่าวิธีการต่างๆที่โพสต์ที่นี่อาจแตกต่างกันอย่างมากโดยคำนึงถึงเวลาที่ใช้ในการทำให้เสร็จ สำหรับฉันฉันมีไฟล์ขนาดเล็กเป็นประจำ เพื่อทดสอบสิ่งที่มีประสิทธิภาพมากที่สุดฉันได้แยก XML ขนาด 5.52 GB (5,933,604,999 ไบต์) เป็นไฟล์แยก 40,693 ไฟล์และวิ่งผ่านคำตอบสามข้อที่ฉันพบที่นี่:
## 5.52 GB (5,933,604,999 bytes) of XML files (40,693 files)
#### Test 1 - Plain Replace
$start = get-date
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName
foreach ($xml in $xmls)
{
(Get-Content $xml).replace("'", " ") | Set-Content $xml
}
$end = get-date
NEW-TIMESPAN –Start $Start –End $End
<#
TotalMinutes: 103.725113128333
#>
#### Test 2 - Replace with -Raw
$start = get-date
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName
foreach ($xml in $xmls)
{
(Get-Content $xml -Raw).replace("'", " ") | Set-Content $xml
}
$end = get-date
NEW-TIMESPAN –Start $Start –End $End
<#
TotalMinutes: 10.1600227983333
#>
#### Test 3 - .NET, System.IO
$start = get-date
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName
foreach ($xml in $xmls)
{
$txt = [System.IO.File]::ReadAllText("$xml").Replace("'"," ")
[System.IO.File]::WriteAllText("$xml", $txt)
}
$end = get-date
NEW-TIMESPAN –Start $Start –End $End
<#
TotalMinutes: 5.83619516833333
#>
เครดิตถึง @ rominator007
ฉันห่อมันเป็นฟังก์ชั่น (เพราะคุณอาจต้องการใช้อีกครั้ง)
function Replace-AllStringsInFile($SearchString,$ReplaceString,$FullPathToFile)
{
$content = [System.IO.File]::ReadAllText("$FullPathToFile").Replace("$SearchString","$ReplaceString")
[System.IO.File]::WriteAllText("$FullPathToFile", $content)
}
หมายเหตุ: นี่ไม่ตรงตามตัวพิมพ์ใหญ่ - เล็ก !!!!!
ดูโพสต์นี้: String แทนที่กรณีที่ไม่สนใจ
สิ่งนี้ใช้ได้กับฉันโดยใช้ไดเรกทอรีการทำงานปัจจุบันใน PowerShell คุณต้องใช้FullName
คุณสมบัติไม่เช่นนั้นจะไม่ทำงานใน PowerShell เวอร์ชัน 5 ฉันต้องเปลี่ยนเวอร์ชั่นเฟรมเวิร์ก. NET เป้าหมายในCSPROJ
ไฟล์ทั้งหมดของฉัน
gci -Recurse -Filter *.csproj |
% { (get-content "$($_.FullName)")
.Replace('<TargetFramework>net47</TargetFramework>', '<TargetFramework>net462</TargetFramework>') |
Set-Content "$($_.FullName)"}
ค่อนข้างเก่าและแตกต่างไปตามที่ฉันต้องการเพื่อเปลี่ยนบรรทัดในทุกกรณีของชื่อไฟล์
ยังSet-Content
ไม่ได้ผลลัพธ์ที่สอดคล้องกันดังนั้นฉันจึงต้องหันไปOut-File
ใช้
รหัสด้านล่าง:
$FileName =''
$OldLine = ''
$NewLine = ''
$Drives = Get-PSDrive -PSProvider FileSystem
foreach ($Drive in $Drives) {
Push-Location $Drive.Root
Get-ChildItem -Filter "$FileName" -Recurse | ForEach {
(Get-Content $_.FullName).Replace($OldLine, $NewLine) | Out-File $_.FullName
}
Pop-Location
}
นี่คือสิ่งที่ดีที่สุดสำหรับฉันใน PowerShell เวอร์ชันนี้:
Major.Minor.Build.Revision
5.1.16299.98
การแก้ไขเล็กน้อยสำหรับคำสั่งชุดเนื้อหา หากไม่พบสตริงการค้นหาSet-Content
คำสั่งจะว่างเปล่า (ว่าง) ไฟล์เป้าหมาย
คุณสามารถตรวจสอบก่อนว่าสตริงที่คุณต้องการมีอยู่หรือไม่ ถ้าไม่มันจะไม่เปลี่ยนอะไร
If (select-string -path "c:\Windows\System32\drivers\etc\hosts" -pattern "String to look for") `
{(Get-Content c:\Windows\System32\drivers\etc\hosts).replace('String to look for', 'String to replace with') | Set-Content c:\Windows\System32\drivers\etc\hosts}
Else{"Nothing happened"}
set-content test.txt "hello hello world hello world hello"
และจากนั้น(get-content .\test.txt).Replace("something", "awesome") | set-content .\test.txt
จะไม่ลบไฟล์ตามที่แนะนำในนี้