编者注:我已经澄清了问题的定义,因为我觉得这个问题很有意思,这个问题值得重新开启。
我有一个包含以下格式的键值行的文本文件 - 请注意,下面的#
行仅用于显示重复块,而不是输入的一部分:
Country:United Kingdom
Language:English
Capital city:London
#
Country:France
Language:French
Capital city:Paris
#
Country:Germany
Language:German
Capital city:Berlin
#
Country:Italy
Language:Italian
Capital city:Rome
#
Country:Russia
Language:Russian
Capital city:Moscow
使用shell命令和实用程序,如何将这样的文件转换为CSV格式,所以它看起来像这样?
Country,Language,Capital city
United Kingdom,English,London
France,French,Paris
Germany,German,Berlin
Italy,Italian,Rome
Russia,Russian,Moscow
换句话说:
[OP的原创]编辑:我的想法是将条目分开,例如:国家:法国将成为法国国家,然后grep / sed标题。但是我不知道如何将标题从一个列移动到几个单独的列。
答案 0 :(得分:4)
包含cut
,paste
和head
的简单解决方案(假设输入文件file
,输出到文件out.csv
):
#!/usr/bin/env bash
{ cut -d':' -f1 file | head -n 3 | paste -d, - - -;
cut -d':' -f2- file | paste -d, - - -; } >out.csv
cut -d':' -f1 file | head -n 3
创建标题行:
cut -d':' -f1 file
从每个输入行提取第一个:
字段,head -n 3
在3行后停止,因为标题重复每3行。
paste -d, - - -
从stdin获取3个输入行(每个-
一个)并将它们组合成一个逗号分隔的输出行(-d,
)
cut -d':' -f2- file | paste -d, - - -
创建数据行:
cut -d':' -f2- file
从每个输入行:
后提取所有内容。
如上所述,paste
然后将3个值组合成一个逗号分隔的输出行。
agc在评论中指出列数(3
)和paste
个操作数(- - -
)是硬编码的上方。
以下解决方案 参数化列数(通过n=...
设置):
{ n=3; pasteOperands=$(printf '%.s- ' $(seq $n))
cut -d':' -f1 file | head -n $n | paste -d, $pasteOperands;
cut -d':' -f2- file | paste -d, $pasteOperands; } >out.csv
printf '%.s- ' $(seq $n)
是一个技巧,可以生成一系列以空格分隔的-
个字符。因为有列($n
)。虽然先前的解决方案现已参数化,但它仍假定列数已提前知道;以下解决方案动态确定列数(由于使用readarray
而需要Bash 4+,但可以使用Bash 3.x):
# Determine the unique list of column headers and
# read them into a Bash array.
readarray -t columnHeaders < <(awk -F: 'seen[$1]++ { exit } { print $1 }' file)
# Output the header line.
(IFS=','; echo "${columnHeaders[*]}") >out.csv
# Append the data lines.
cut -d':' -f2- file | paste -d, $(printf '%.s- ' $(seq ${#columnHeaders[@]})) >>out.csv
awk -F: 'seen[$1]++ { exit } { print $1 }
输出每个输入行的列名(第一个:
- 分隔字段),记住关联数组seen
中的列名,并在第一列名称处停止 second 时间。
readarray -t columnHeaders
逐行将awk
的输出读入数组columnHeaders
(IFS=','; echo "${columnHeaders[*]}") >out.csv
使用空格作为分隔符(通过$IFS
指定)打印数组元素;请注意使用子shell((...)
)以便本地化修改$IFS
的效果,否则会产生全局影响。
cut ...
管道使用与以前相同的方法,paste
的操作数基于数组columnHeaders
的元素数创建({{1} })。
将上述内容包含在输出到stdout的函数中,也适用于Bash 3.x :
${#columnHeaders[@]}
答案 1 :(得分:1)
使用datamash
,tr
和join
:
datamash -t ':' -s -g 1 collapse 2 < country.txt | tr ',' ':' | \
datamash -t ':' transpose | \
join -t ':' -a1 -o 1.2,1.3,1.1 - /dev/null | tr ':' ','
输出:
Country,Language,Capital city
United Kingdom,English,London
France,French,Paris
Germany,German,Berlin
Italy,Italian,Rome
Russia,Russian,Moscow
以上代码的一个缺陷,即datamash
输出排序,并且需要未排序(恢复到原始顺序)使用了编码join
命令。这个令人讨厌的前置单行(修订版待定,无需解包)是第一次尝试自动化 unsort (rev
的散列,{{ 1}},nl
,sort
,cut
和tr
):
sed
答案 2 :(得分:1)
我的bash脚本将是:
#!/bin/bash
count=0
echo "Country,Language,Capital city"
while read line
do
(( count++ ))
(( count -lt 3 )) && printf "%s," "${line##*:}"
(( count -eq 3 )) && printf "%s\n" "${line##*:}" && (( count = 0 ))
done<file
<强>输出强>
Country,Language,Capital city
United Kingdom,English,London
France,French,Paris
Germany,German,Berlin
Italy,Italian,Rome
Russia,Russian,Moscow
修改强>
将[ stuff ]
替换为(( stuff ))
,test
替换为double parenthesis
Public Sub Command4_Click()
Dim myProfiling As Recordset
Set myProfiling = CurrentDb.OpenRecordset("Profiling")
varChangePicture = Forms!sfrChangeProfilePics!FileName.value
DoCmd.Close
Forms![Main Form].[Crafter Default].Form!sfrProfiling.Form!pic.value=varChangePicture
End Sub
。
答案 3 :(得分:0)
您还可以编写一个稍微更通用的bash脚本版本,该脚本可以获取保存数据的重复行数并在此基础上生成输出,以避免对标头值进行硬编码并处理其他字段。 (您也可以只扫描第一次重复的字段名称,并以这种方式设置重复行。)
#!/bin/bash
declare -i rc=0 ## record count
declare -i hc=0 ## header count
record=""
header=""
fn="${1:-/dev/stdin}" ## filename as 1st arg (default: stdin)
repeat="${2:-3}" ## number of repeating rows (default: 3)
while read -r line; do
record="$record,${line##*:}"
((hc == 0)) && header="$header,${line%%:*}"
if ((rc < (repeat - 1))); then
((rc++))
else
((hc == 0)) && { printf "%s\n" "${header:1}"; hc=1; }
printf "%s\n" "${record:1}"
record=""
rc=0
fi
done <"$fn"
有很多方法可以解决这个问题。您将不得不尝试找到最有效的数据文件大小等。无论您使用脚本还是shell工具的组合,cut
,paste
等都是大的留给你的程度。
<强>输出强>
$ bash readcountry.sh country.txt
Country,Language,Capital city
United Kingdom,English,London
France,French,Paris
Germany,German,Berlin
Italy,Italian,Rome
Russia,Russian,Moscow
输出4个字段
添加Population
字段的示例输入文件:
$ cat country2.txt
Country:United Kingdom
Language:English
Capital city:London
Population:20000000
<snip>
输出
$ bash readcountry.sh country2.txt 4
Country,Language,Capital city,Population
United Kingdom,English,London,20000000
France,French,Paris,10000000
Germany,German,Berlin,150000000
Italy,Italian,Rome,9830000
Russia,Russian,Moscow,622000000