Показать полную графическую версию : [решено] Поиск в csv файле
ejik_off
23-12-2019, 17:12
Здравствуйте, гуру PS!!!
Есть файл тхт в нем содержатся логины:
ppetrov
iivanov
ssidorov
...
и есть csv файл в формате samaccountname,count
samaccountname,count
ppetrov,1
iivanov,1
ssidorov,2
нужно сделать поиск, т.е логины из тхт файла сравнить с логинами из csv-файла если логин найден, то поле count увеличить на 1, если не найдено то добавить в конец файла в поле samaccountname: логин и в count: 1
$txt = get-content c:\tmp\usr.txt
$csv = import-csv c:\tmp\usr_list.csv -Delimiter ","
foreach($user_txt in $txt) {
foreach($user_csv in $csv){
if($user_txt -eq $user_csv.samaccountname) {
как изменить колонку count?
}
else {$user_txt,1 | out-file c:\tmp\usr_list.csv -Append - тоже не знаю как правильно вставить}
}
}
ejik_off, вот взяли бы, да приложили в архиве и тот, и другой файл для облегчения работы отвечающим.
Попробуйте так:
$sTxtFile = 'C:\Мои проекты\0289\0001.txt'
$sCSVFile = 'C:\Мои проекты\0289\0002.csv'
$sTxtContent = Get-Content -Path $sTxtFile
$sCSVContent = Import-Csv -Path $sCSVFile
$sTxtContent | ForEach-Object -Process {
$oCSVObject = $sCSVContent | Where-Object -Property 'samaccountname' -eq -Value $_
if($oCSVObject) {
$oCSVObject.count = [System.String]([System.Int32]($oCSVObject.count) + 1)
} else {
$sCSVContent += [PSCustomObject]@{samaccountname = $_; count = '1'}
}
}
$sCSVContent | ConvertTo-Csv -NoTypeInformation | ForEach-Object -Process {
$_.Replace('"','')
} | Set-Content -Path $sCSVFile -Force
ejik_off
23-12-2019, 20:38
ejik_off, вот взяли бы, да приложили в архиве и тот, и другой файл для облегчения работы отвечающим. »
Прошу прощения, не подумал даже о прикрепе фалов. :sorry:
Огромное спасибо за скрипт, работает!!!! :up
greg zakharov
23-12-2019, 21:27
Iska, венгерская нотация в PowerShell? Переутомление? Да и лучше сделать так (PowerShell v6):
$csv, $txt = (Import-Csv .\input.csv -Delimiter ','), (Get-Content .\input.txt)
$txt.ForEach{
.({ $csv += [PSCustomObject]@{samaccountname = $_; count = 1} },{
++[Int32]$csv[$csv.samaccountname.IndexOf($_)].count
})[[Byte]($_ -in $csv.samaccountname)]
}
($csv | ConvertTo-Csv) -replace '"' | Out-File .\input.csv
Результат теста производительности:
time {
$csv, $txt = (Import-Csv .\input.csv -Delimiter ','), (Get-Content .\input.txt)
$txt.ForEach{
.({ $csv += [PSCustomObject]@{samaccountname = $_; count = 1} },{
++[Int32]$csv[$csv.samaccountname.IndexOf($_)].count
})[[Byte]($_ -in $csv.samaccountname)]
}
($csv | ConvertTo-Csv) -replace '"' | Out-File .\input.csv
}, {
$sTxtFile = '.\input1.txt'
$sCSVFile = '.\input1.csv'
$sTxtContent = Get-Content -Path $sTxtFile
$sCSVContent = Import-Csv -Path $sCSVFile
$sTxtContent | ForEach-Object -Process {
$oCSVObject = $sCSVContent | Where-Object -Property 'samaccountname' -eq -Value $_
if($oCSVObject) {
$oCSVObject.count = [System.String]([System.Int32]($oCSVObject.count) + 1)
} else {
$sCSVContent += [PSCustomObject]@{samaccountname = $_; count = '1'}
}
}
$sCSVContent | ConvertTo-Csv -NoTypeInformation | ForEach-Object -Process {
$_.Replace('"','')
} | Set-Content -Path $sCSVFile -Force
}
# итог:
Total ms: 37.7992
Total ms: 108.8279
Как говорится, почувствуйте разницу.
ejik_off
24-12-2019, 05:43
greg zakharov, классно, осталось понять как он работает )))
ejik_off
24-12-2019, 07:53
на рабочем компе скрипт не работает, видно версии PS разные. На раб.станции версия 5.1.17763.771
Сбой вызова метода из-за отсутствия в [System.Management.Automation.PSObject] метода с именем "op_Addition".
строка:12 знак:8
+ $sCSVContent += [PSCustomObject]@{samaccountname = $_; count = ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (op_Addition:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
как адаптировать скрипт?
DJ Mogarych
24-12-2019, 08:55
ejik_off, используйте первый вариант. Вряд ли в данном случае разница в 70 миллисекунд имеет большое значение.
ejik_off
24-12-2019, 09:11
оба варианта дают такую ошибку
DJ Mogarych
24-12-2019, 09:36
ejik_off, дайте примеры файлов, хоть будет на чём протестировать.
ejik_off
24-12-2019, 10:13
прикрепил архив
DJ Mogarych
24-12-2019, 11:15
$txt = gc D:\temp\test\Users.txt
[array]$csv = import-csv D:\temp\test\user_list.csv
foreach ($login in $txt) {
if ($login -notin $csv.samaccountname) {
$csv += [pscustomobject]@{samaccountname="$login";count="1"}
}
else {([double]($csv |? samaccountname -eq "$login").count)++}
}
$csv |export-csv D:\temp\test\user_list.csv -NoTypeInformation
ejik_off
24-12-2019, 12:21
DJ Mogarych, спасибо, работает!
ejik_off, это потому, что у Вас в примере в csv-файле одна строка. Я всё время забываю, что нужно постоянно отслеживать эти долбаные просчёты в архитектуре милые шалости PowerShell'a.
Замените в том коде эту строку:
$sCSVContent = Import-Csv -Path $sCSVFile
на эту:
$sCSVContent = , (Import-Csv -Path $sCSVFile)
DJ Mogarych
25-12-2019, 08:48
А, так вот в чём дело - а я и думаю, почему в csv просто так нельзя было добавить строку, и надо было принудительно указывать, что это массив, а не кастом-объект. В принципе, логично - PS что видит, так и интерпретирует, объект один, так почему это должно быть массивом?
greg zakharov
25-12-2019, 10:38
Какие все однако переутомившиеся: сперва венгерская нотация, потом бредни про массив...
$c = Import-Csv .\input.csv
$c.GetType() # возвращает Object[]
В принципе, логично - PS что видит, так и интерпретирует, объект один, так почему это должно быть массивом? »
Я бы предпочёл, чтобы это был массив из одного элемента — вот это было бы вполне логично. А когда тип возвращаемого командлетом значения зависит от количества данных — это, простите, совсем нелогично. И по факту на каждый такой случай всё равно приходится при кодировании либо «лепить» подобную конструкцию, либо отдельно учитывать случай с возвращаемым базовым типом вместо массива.
Ладно, я понимаю, когда совсем нет элементов — тут деваться некуда. Это надо проверять. Так было с незапамятных времён, так и осталось. Но PowerShell теперь к этим двум вариантам добавляет ещё и третий вариант, абсолютно бессмысленный с точки зрения программирования, но который тоже приходится учитывать.
Может быть, в шестой версии, которая у коллеги greg zakharov, это уже не так, и для одной строки командлет Import-Csv возвращает именно массив из одного элемента. Как и прочие командлеты в аналогичных случаях. Дай-то бог. Но у меня, в 5.1, пока что так:
https://i.imgur.com/PUIWxrZ.png
greg zakharov
25-12-2019, 20:46
Iska, специально для вас:
E:\sandbox
λ $PSVersionTable.PSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 18362 145
E:\sandbox
λ $c = Import-Csv .\input.txt
E:\sandbox
λ $c.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
E:\sandbox
λ $c[0]
samaccountname count
-------------- -----
ppetrov 1
E:\sandbox
λ $c[0].samaccountname
ppetrov
Тот же результат будет и в шестерке и в семерке.
Petya V4sechkin
25-12-2019, 21:58
И да, вы непременно удалите это сообщение, сославшись на якобы нарушение форумных правил.
Я удалю ваши сообщения, поскольку они явным образом нарушают пункты 3.1 и 3.7 общих правил (http://forum.oszone.net/rules.html) форума.
Я бы предпочёл, чтобы это был массив из одного элемента — вот это было бы вполне логично. »
А если потребуется не массив, а единственный элемент, сохраняемый с определенным типом? Ситуации бывают разные, иногда, как раз, требуется ограничить данные, чтобы получался на выходе не массив.
А когда тип возвращаемого командлетом значения зависит от количества данных — это, простите, совсем нелогично. »
При наличии функциональности автоматического приведения типов, это, скорее, логично. И не в командлете загвоздка, кстати...
И по факту на каждый такой случай всё равно приходится при кодировании либо «лепить» подобную конструкцию, либо отдельно учитывать случай с возвращаемым базовым типом вместо массива. »
Всё гораздо проще:
Если требуется таки массив, при любом количестве данных, то необходимо указать тип переменной, либо:
http://ipic.su/img/img7/tn/Snimokekrana20191226102702.1577334443.png (http://ipic.su/img/img7/fs/Snimokekrana20191226102702.1577334443.png)
для одной строки командлет Import-Csv возвращает именно массив из одного элемента. Как и прочие командлеты в аналогичных случаях. »
Командлет, в данном случае, не при чем, так работало:
http://ipic.su/img/img7/tn/Snimokekrana20191226103159.1577334736.png (http://ipic.su/img/img7/fs/Snimokekrana20191226103159.1577334736.png)
и продолжает работать автоматическое приведение типов:
http://ipic.su/img/img7/tn/Snimokekrana20191226103320.1577334824.png (http://ipic.su/img/img7/fs/Snimokekrana20191226103320.1577334824.png)
И кроме того, есть ведь специальный оператор массива:
The array operator is useful in scripts when you are getting objects, but do not know how many objects you get. (https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_arrays?view=powershell-6)
$p = @(Get-Process Notepad)
Так что логика, определенно, присутствует... к ней просто надо привыкнуть.
Я бы предпочёл, чтобы это был массив из одного элемента — вот это было бы вполне логично. А когда тип возвращаемого командлетом значения зависит от количества данных — это, простите, совсем нелогично. И по факту на каждый такой случай всё равно приходится при кодировании либо «лепить» подобную конструкцию, либо отдельно учитывать случай с возвращаемым базовым типом вместо массива. »
Тип не зависит от количества. Практически все командлеты написаны под функциональный стиль использования - они не "возвращают", они выдают результат в первый поток. Это вы своим любимым императивным подходом обращаетесь к неявным преобразованиям типов.
© OSzone.net 2001-2012
vBulletin v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.