in.txt
我有一个包含 3 列(Doc_Title
、Doc_Date
、 )的CSV 文件Doc_URL
,我想使用 PS使用另一个包含 2 列(、 )的Doc_Category
CSV 文件添加另一列。我在 Powershell 中已经有一个管道,它创建了,现在我想知道如何以最简单的方式解决这个问题。在 Linux 中,我可以使用tag.txt
keyword
tag
in.txt
sed和awk来做到这一点,但是 power-shell 实现这一目标的方法是什么?
示例in.txt
可以是(为了便于阅读,显示为 TSV):
/pub/howto.en.pdf 1980-01-01 An easy introduction
/pub/howto.de.pdf 1980-01-01 Eine einfache Einführung
/pub/howto.fr.pdf 1980-01-01 Une introduction simple
/lit/intro.en.pdf 1980-01-01 Literature review
/lit/intro.pdf 1980-01-01 Revue de littérature
/foo/intro.pdf 1980-01-01 Literatur-Review
具有以下标签定义tag.txt
(再次以固定宽度显示以便于阅读):
pub Publication
lit Literature
.en.pdf English
.fr.pdf French
.de.pdf German
我希望获得
/pub/howto.en.pdf 1980-01-01 An easy introduction Publication, English
/pub/howto.de.pdf 1980-01-01 Eine einfache Einführung Publication, German
/pub/howto.fr.pdf 1980-01-01 Une introduction simple Publication, French
/lit/intro.en.pdf 1980-01-01 Literature review Literature, English
/lit/intro.pdf 1980-01-01 Revue de littérature Literature
/foo/intro.pdf 1980-01-01 Literatur-Review
我很困惑如何在 powershell 中实现这一点,因此非常感谢任何提示。
有关的
答案1
您可以尝试以下操作:
假设您有一个名为 in.txt 的文件位于 path/to/in.txt,其中包含以下内容:
"Doc_Title","Doc_Date","Doc_URL"
"/pub/howto.en.pdf","1980-01-01","An easy introduction"
"/pub/howto.de.pdf","1980-01-01","Eine einfache Einführung"
"/pub/howto.fr.pdf","1980-01-01","Une introduction simple"
"/lit/intro.en.pdf","1980-01-01","Literature review"
"/lit/intro.pdf","1980-01-01","Revue de littérature"
"/foo/intro.pdf","1980-01-01","Literatur-Review"
并且您想将结果导出到位于 path/to/out.csv 的 .csv 文件,您可以使用以下代码:
$types=@{'pub'='Publication';'lit'='Literature'}
$languages=@{'.en.pdf'='English';'.fr.pdf'='French';'.de.pdf'='German'}
$rows=import-csv "path/to/in.txt"
$table=foreach ($row in $rows) {
$title=$row.Doc_Title
$date=$row.Doc_Date
$url=$row.Doc_URL
$type=($title | Select-String -pattern "(?<=\/)([\w]{3})(?=\/)").matches.value
$type=$types.$type
$language=($title | Select-String -pattern "(\.\w{2}\.pdf)").matches.value
$language=$languages.$language
[PSCustomObject]@{Doc_Title=$title;Doc_Date=$date;Doc_URL=$url;Doc_Type=$type;Doc_Lang=$language}
}
$table | export-csv "path/to/out.csv"
请务必尝试我的代码并告诉我它是否给出了您想要的结果。
我的代码非常简单,非常清晰,可读性强。我认为最好你自己去弄清楚,这样你才能完全理解。我不喜欢填鸭式的指导。
一些说明:
1、我认为最好让文件扩展名与内容的格式保持一致,尽管它是纯文本文件,但它是有结构的,而不是任何.txt文件,所以我认为最好将CSV文件的扩展名设为.csv...
2、我认为将两个值放在一列中的想法是错误的,CSV 代表逗号分隔值,值由逗号分隔,因此最好不要将逗号放入值中,并将两个值保留为单独的列而不是一列。
3、我给您的正则表达式与您提供的示例配合得很好,类型识别的正则表达式接受斜杠之间的三个单词字符(字母和数字),语言识别的正则表达式接受点之间的两个单词字符,如果需要,可以调整正则表达式。
最低 PowerShell 版本要求:未知,但是我只在 PowerShell 7.1.1 上测试了我的代码,我不知道我的代码是否在较低版本上运行,但使用最新的软件总是好的。
更新
如果 中的斜杠之间只有三个字符Doc_Title
,则可以使用此方法获取斜杠之间的三个字母:
$title.substring(1,3)
仅当斜杠之间恰好有三个字母(并且第一个斜杠位于字段的开头)时,此方法才会起作用。
您可以使用它来获取类似的字符串.en.pdf
$title.substring(($title.length-7),7)
这只有满足两个条件时才会起作用:首先, 中必须存在此字符串$title
,并且点之间必须恰好有两个字符。
我修改了我的代码以生成您想要的结果:
$types=@{'pub'='Publication';'lit'='Literature'}
$languages=@{'.en.pdf'='English';'.fr.pdf'='French';'.de.pdf'='German'}
$rows=import-csv "path/to/in.csv"
foreach ($row in $rows) {
$title=$row.Doc_Title
$date=$row.Doc_Date
$url=$row.Doc_URL
$category=$types.$(($title | Select-String -pattern "(?<=\/)([\w]+)(?=\/)").matches.value)
if ($title -match "(\.[\w]+\.pdf)"){$category=$category+","+$languages.$(($title | Select-String -pattern "(\.[\w]+\.pdf)").matches.value)}
[PSCustomObject]@{Doc_Title=$title;Doc_Date=$date;Doc_URL=$url;Doc_Category=$category} | export-csv -path "path/to/out.csv" -NoTypeInformation -append
}
示例输出:
"Doc_Title","Doc_Date","Doc_URL","Doc_Category"
"/pub/howto.en.pdf","1980-01-01","An easy introduction","Publication,English"
"/pub/howto.de.pdf","1980-01-01","Eine einfache Einführung","Publication,German"
"/pub/howto.fr.pdf","1980-01-01","Une introduction simple","Publication,French"
"/lit/intro.en.pdf","1980-01-01","Literature review","Literature,English"
"/lit/intro.pdf","1980-01-01","Revue de littérature","Literature"
"/foo/intro.pdf","1980-01-01","Literatur-Review",
我使用的正则表达式将匹配任意数量的单词字符(a-zA-Z0-9_),它们与您给出的示例配合良好,但如果您的字符串包含非单词字符则不起作用,请根据需要进行调整。
为什么我没有首先创建一个带逗号的字段,请参阅维基百科
简而言之,CSV 文件格式并非完全标准化,对于 CSV 应该是什么样子并没有达成共识,不同的 CSV 实现可能会或可能不会允许字段中使用逗号,因此字段中的逗号可能会或可能不会破坏格式。
答案2
只是为了好玩,下面是解析 URL 以获取标签的另一种策略:
$Tags = @'
id,text
pub,Publication
lit,Literature
en,English
fr,French
de,German
'@ | ConvertFrom-Csv | ForEach { $hash = @{} } {
$hash.Add( $_.ID, $_.Text )
} { $hash }
@'
Doc_URL,Doc_Date,Doc_Title
/pub/howto.en.pdf,1980-01-01,An easy introduction
/pub/howto.de.pdf,1980-01-01,Eine einfache Einführung
/pub/howto.fr.pdf,1980-01-01,Une introduction simple
/lit/intro.en.pdf,1980-01-01,Literature review
/lit/intro.pdf,1980-01-01,Revue de littérature
/foo/intro.pdf,1980-01-01,Literatur-Review
'@ | ConvertFrom-CSV | ForEach {
$Doc_Tags = @( $Tags[$_.Doc_URL.Split('/')[1]] , $Tags[$_.Doc_URL.Split('.')[-2]] ) -ne $null -join ', '
[PSCustomObject]@{
'Doc_URL' = $_.Doc_URL
'Doc_Date' = $_.Doc_Date
'Doc_Title' = $_.Doc_Title
'Doc_Tags' = $Doc_Tags
}
} | Export-Csv $env:Temp\out.csv -NoTypeInformation
Import-Csv $env:Temp\out.csv
该<*Here-String*> | ConvertFrom-Csv
构造可以用Import-Csv <FileName>
语句替换:
$TagFIle = c:\Tag.txt
$InFIle = c:\In.txt
$Tags = Import-Csv $TagFIle | ForEach { $hash = @{} } {
$hash.Add( $_.ID, $_.Text )
} { $hash }
Import-Csv $InFIle | ForEach {
$Doc_Tags = @( $Tags[$_.Doc_URL.Split('/')[1]] , $Tags[$_.Doc_URL.Split('.')[-2]] ) -ne $null -join ', '
[PSCustomObject]@{
'Doc_URL' = $_.Doc_URL
'Doc_Date' = $_.Doc_Date
'Doc_Title' = $_.Doc_Title
'Doc_Tags' = $Doc_Tags
}
} | Export-Csv $env:Temp\out.csv -NoTypeInformation
Import-Csv $env:Temp\out.csv