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)
आवश्यक है:
कोष्ठक के बिना सामग्री को पढ़ा जाता है, एक समय में एक पंक्ति, और पाइप लाइन से नीचे तब तक बहती है जब तक वह आउट-फाइल या सेट-सामग्री तक नहीं पहुंच जाती है, जो एक ही फ़ाइल को लिखने की कोशिश करता है, लेकिन यह पहले से ही गेट-कंटेंट द्वारा खुला है और आपको मिलता है एक त्रुटि। कोष्ठक एक बार (खुले, पढ़ने और बंद) प्रदर्शन करने के लिए सामग्री पढ़ने के संचालन का कारण बनता है। तभी जब सभी लाइनों को पढ़ा गया है, उन्हें एक समय में एक पाइप किया जाता है और जब वे पाइप लाइन में अंतिम कमांड तक पहुंचते हैं तो उन्हें फ़ाइल में लिखा जा सकता है। यह $ सामग्री = सामग्री के समान है; $ सामग्री | कहाँ पे ...
Set-Content
बजाय का उपयोग करके Out-File
एक चेतावनी मिलती है जैसे "यह प्रक्रिया '123.csv' फ़ाइल तक नहीं पहुंच सकती है क्योंकि इसका उपयोग किसी अन्य प्रक्रिया द्वारा किया जा रहा है।" ।
Get-Content
कोष्ठक में यह काम करता है। क्या आप अपने उत्तर में बता सकते हैं कि कोष्ठक क्यों आवश्यक है? मैं अभी भी जगह लेंगे Out-File
साथ Set-Content
है क्योंकि यह सुरक्षित है; यदि आप कोष्ठक को भूल जाते हैं तो यह लक्ष्य फ़ाइल को मिटा देने से बचाता है।
Set-Content
बजाय का उपयोग करना Out-File
बेहतर और सुरक्षित समाधान है। क्षमा करना है।
मैं निम्न उदाहरण में देखी गई .NET की फाइल-क्लास और उसके स्थिर तरीकों का उपयोग करना पसंद करता हूं।
$content = [System.IO.File]::ReadAllText("c:\bla.txt").Replace("[MYID]","MyValue")
[System.IO.File]::WriteAllText("c:\bla.txt", $content)
इसमें गेट-कंटेंट के साथ स्ट्रिंग-एरे के बजाय सिंगल स्ट्रिंग के साथ काम करने का फायदा है । तरीकों का भी ध्यान रखें कि आप ज्यादातर समय ध्यान रखे बिना फाइल (UTF-8 BOM इत्यादि) के एन्कोडिंग का ध्यान रखें।
इसके अलावा तरीके सेट-कंटेंट के जरिए गेट-कंटेंट और पाइपिंग का उपयोग करते हुए एल्गोरिथ्म के विपरीत लाइन एंडिंग (यूनिक्स लाइन एंडिंग का उपयोग किया जा सकता है) को गड़बड़ नहीं करते हैं ।
इसलिए मेरे लिए: कुछ चीजें जो वर्षों में टूट सकती हैं।
.NET कक्षाओं का उपयोग करते समय एक छोटी सी ज्ञात बात यह है कि जब आपने PowerShell विंडो में "[System.IO.File] ::" टाइप किया है, तो आप 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-ChildItem $Directory -File -Recurse | ForEach { (Get-Content $_.FullName) | ForEach { $_ -replace '[MYID]', 'MyValue' } | Set-Content $_.FullName }
जहाँ $Directory
निर्देशिका उन फ़ाइलों से युक्त होती है जिन्हें आप संशोधित करना चाहते हैं।
यह वही है जो मैं उपयोग करता हूं, लेकिन यह बड़ी पाठ फ़ाइलों पर धीमा है।
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()
}
}
(ऊपर दिए गए कोड का परीक्षण नहीं किया गया है।)
किसी दस्तावेज़ में पाठ को बदलने के लिए स्ट्रीमराइडर और स्ट्रीमराइटर का उपयोग करने के लिए संभवतः अधिक सुरुचिपूर्ण तरीका है, लेकिन यह आपको एक अच्छा प्रारंभिक बिंदु देना चाहिए।
मुझे पेलेट के विंडोज पॉवरशेल इन एक्शन से करने के लिए थोड़ा ज्ञात लेकिन आश्चर्यजनक रूप से अच्छा तरीका मिला । आप $ env: पथ के समान चर जैसी फ़ाइलों का संदर्भ दे सकते हैं, लेकिन आपको घुंघराले ब्रेस को जोड़ने की आवश्यकता है।
${c:file.txt} = ${c:file.txt} -replace 'oldvalue','newvalue'
$myFile
?
$a = 'file.txt'; invoke-expression "`${c:$a} = `${c:$a} -replace 'oldvalue','newvalue'"
यदि आपको कई फाइलों में स्ट्रिंग्स को बदलने की आवश्यकता है:
यह ध्यान दिया जाना चाहिए कि यहां तैनात विभिन्न तरीकों को पूरा होने में लगने वाले समय के संबंध में बेतहाशा भिन्न हो सकते हैं। मेरे लिए, मेरे पास नियमित रूप से बड़ी संख्या में छोटी फाइलें हैं। सबसे अधिक प्रदर्शन करने वाले का परीक्षण करने के लिए, मैंने XML की 402693 अलग-अलग फाइलों में 5.52 जीबी (5,933,604,999 बाइट्स) निकाले और तीन उत्तर मुझे यहां मिले
## 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.Replace मामले की अनदेखी
यह मेरे लिए PowerShell में वर्तमान कार्यशील निर्देशिका का उपयोग करके काम करता है। आपको FullName
संपत्ति का उपयोग करने की आवश्यकता है , या यह PowerShell संस्करण 5 में काम नहीं करेगा। मुझे अपनी सभी CSPROJ
फ़ाइलों में लक्ष्य .NET फ्रेमवर्क संस्करण को बदलने की आवश्यकता है ।
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
फाइल खाली नहीं होगी।