Воссоздание контрольной суммы linux md5 в Windows
Я провожу довольно простые контрольные суммы для наших Linux-серверов, но теперь мне нужно воссоздать нечто подобное для наших пользователей Windows. Чтобы получить единственную контрольную сумму, я просто запускаю:
md5sum *.txt | awk '{ print $1 }' | md5sum
Я изо всех сил пытаюсь воссоздать это в Windows с помощью командного файла или Powershell. Ближайшее, что у меня есть:
Get-ChildItem $path -Filter *.txt |
Foreach-Object {
$hash = Get-FileHash -Algorithm MD5 -Path ($path + "\" + $_) | Select -ExpandProperty "Hash"
$hash = $hash.tolower() #Get-FileHash returns checksums in uppercase, linux in lower case (!)
Write-host $hash
}
Это будет выводить на консоль те же результаты контрольной суммы для каждого файла, что и команда linux, но я ускользаю от меня, передавая это обратно в Get-FileHash для получения единственного вывода, который соответствует эквиваленту Linux. При записи в файл я сталкиваюсь с различиями в возврате каретки
Потоковая передача в виде строки обратно в Get-FileHash не возвращает ту же контрольную сумму:
$String = Get-FileHash -Algorithm MD5 -Path (Get-ChildItem -path $files -Recurse) | Select -ExpandProperty "Hash"
$stringAsStream = [System.IO.MemoryStream]::new()
$writer = [System.IO.StreamWriter]::new($stringAsStream)
$writer.write($stringAsStream)
Get-FileHash -Algorithm MD5 -InputStream $stringAsStream
Я не слишком разбираюсь в этом? Я уверен, что это не должно быть так сложно! TIA
2 ответа
Дьявол кроется в деталях:
- (уже известно)
Get-FileHash
возвращает контрольные суммы в верхнем регистре, в то время как Linuxmd5sum
в нижнем регистре (!); - Фильтр поставщика файловой системы не чувствителен к регистру в PowerShell, в то время как в Linux зависит от параметра
nocaseglob
. Если установлено (shopt -s nocaseglob
), то Bash сопоставляет имена файлов без учета регистра при расширении имени файла. В противном случае (shopt -u nocaseglob
), сопоставление имени файла чувствительно к регистру ; - Заказ:
Get-ChildItem
вывод упорядочивается в соответствии с алгоритмом сопоставления Unicode, а в Linux*.txt
фильтр расширяется в порядкеLC_COLLATE
категория (LC_COLLATE="C.UTF-8"
в моей системе).
В следующем (частично прокомментированном) сценарии три
# Test
блоки демонстрируют мои шаги отладки до окончательного решения:
Function Get-StringHash {
[OutputType([System.String])]
param(
# named or positional: a string
[Parameter(Position=0)]
[string]$InputObject
)
$stringAsStream = [System.IO.MemoryStream]::new()
$writer = [System.IO.StreamWriter]::new($stringAsStream)
$writer.write( $InputObject)
$writer.Flush()
$stringAsStream.Position = 0
Get-FileHash -Algorithm MD5 -InputStream $stringAsStream |
Select-Object -ExpandProperty Hash
$writer.Close()
$writer.Dispose()
$stringAsStream.Close()
$stringAsStream.Dispose()
}
function ConvertTo-Utf8String {
[OutputType([System.String])]
param(
# named or positional: a string
[Parameter(Position=0, Mandatory = $false)]
[string]$InputObject = ''
)
begin {
$InChars = [char[]]$InputObject
$InChLen = $InChars.Count
$AuxU_8 = [System.Collections.ArrayList]::new()
}
process {
for ($ii= 0; $ii -lt $InChLen; $ii++) {
if ( [char]::IsHighSurrogate( $InChars[$ii]) -and
( 1 + $ii) -lt $InChLen -and
[char]::IsLowSurrogate( $InChars[1 + $ii]) ) {
$s = [char]::ConvertFromUtf32(
[char]::ConvertToUtf32( $InChars[$ii], $InChars[1 + $ii]))
$ii ++
} else {
$s = $InChars[$ii]
}
[void]$AuxU_8.Add(
([System.Text.UTF32Encoding]::UTF8.GetBytes($s) |
ForEach-Object { '{0:X2}' -f $_}) -join ''
)
}
}
end { $AuxU_8 -join '' }
}
# Set variables
$hashUbuntu = '5d944e44149fece685d3eb71fb94e71b'
$hashUbuntu <# copied from 'Ubuntu 20.04 LTS' in Wsl2:
cd `wslpath -a 'D:\\bat'`
md5sum *.txt | awk '{ print $1 }' | md5sum | awk '{ print $1 }'
<##>
$LF = [char]0x0A # Line Feed (LF)
$path = 'D:\Bat' # testing directory
$filenames = 'D:\bat\md5sum_Ubuntu_awk.lst'
<# obtained from 'Ubuntu 20.04 LTS' in Wsl2:
cd `wslpath -a 'D:\\bat'`
md5sum *.txt | awk '{ print $1 }' > md5sum_Ubuntu_awk.lst
md5sum md5sum_Ubuntu_awk.lst | awk '{ print $1 }' # for reference
<##>
# Test #1: is `Get-FileHash` the same (beyond character case)?
$hashFile = Get-FileHash -Algorithm MD5 -Path $filenames |
Select-Object -ExpandProperty Hash
$hashFile.ToLower() -ceq $hashUbuntu
# Test #2: is `$stringToHash` well-defined? is `Get-StringHash` the same?
$hashArray = Get-Content $filenames -Encoding UTF8
$stringToHash = ($hashArray -join $LF) + $LF
(Get-StringHash -InputObject $stringToHash) -eq $hashUbuntu
# Test #3: another check: is `Get-StringHash` the same?
Push-Location -Path $path
$filesInBashOrder = bash.exe -c "ls -1 *.txt"
$hashArray = $filesInBashOrder |
Foreach-Object {
$hash = Get-FileHash -Algorithm MD5 -Path (
Join-Path -Path $path -ChildPath $_) |
Select-Object -ExpandProperty "Hash"
$hash.tolower()
}
$stringToHash = ($hashArray -join $LF) + $LF
(Get-StringHash -InputObject $stringToHash) -eq $hashUbuntu
Pop-Location
# Solution - ordinal order assuming `LC_COLLATE="C.UTF-8"` in Linux
Push-Location -Path $path
$hashArray = Get-ChildItem -Filter *.txt -Force -ErrorAction SilentlyContinue |
Where-Object {$_.Name -clike "*.txt"} | # only if `shopt -u nocaseglob`
Sort-Object -Property { (ConvertTo-Utf8String -InputObject $_.Name) } |
Get-FileHash -Algorithm MD5 |
Select-Object -ExpandProperty "Hash" |
Foreach-Object {
$_.ToLower()
}
$stringToHash = ($hashArray -join $LF) + $LF
(Get-StringHash -InputObject $stringToHash).ToLower() -ceq $hashUbuntu
Pop-Location
Вывод (проверено на 278 файлах):
.\SO\69181414.ps1
5d944e44149fece685d3eb71fb94e71b
True
True
True
True
Вам нужно сослаться на
.Hash
свойство возвращенного объекта из
Get-FileHash
. Если вы хотите похожий вид на
md5hash
, вы также можете использовать
Select-Object
чтобы курировать это:
# Get filehashes in $path with similar output to md5sum
$fileHashes = Get-ChildItem $path -File | Get-FileHash -Algorithm MD5
# Once you have the hashes, you can reference the properties as follows
# .Algorithm is the hashing algo
# .Hash is the actual file hash
# .Path is the full path to the file
foreach( $hash in $fileHashes ){
"$($hash.Algorithm):$($hash.Hash) ($($hash.Path))"
}
Для каждого файла в
$path
, над
foreach
цикл создаст строку, похожую на:
MD5:B4976887F256A26B59A9D97656BF2078 (C:\Users\username\dl\installer.msi)
Очевидно, что алгоритм, хэш и имена файлов будут отличаться в зависимости от выбранного алгоритма хеширования и файловой системы.