问题
我收到了一个以管道符分隔的文本文件,其中包含每个文件的文件名和一些索引信息。我的目标是将其变成以制表符分隔的文件。 然而,我想知道空条目在哪里。例如,使用lorem||dolor
将变为lorem
'\t'
<empty>
'\t'
dolor
。
让我再举几个例子,说明我收到了什么以及需要什么:
多行示例:(注意,每行上的条目数量相同。)
给定:
||dolor|sit
amet,||adipiscing|
sed|do|eiusmod|tempor
希望实现的目标:
<empty> '\t' <empty> '\t' dolor '\t' sit '\n'
amet, '\t' <empty> '\t' adipiscing '\t' <empty> '\n'
sed '\t' do '\t' eiusmod '\t' tempor '\n'
开头和结尾的空条目。
给定:
|ut|labore||dolore||
希望实现的目标:
<empty> '\t' ut '\t' labore '/t' <empty> '\t' dolore '\t' <empty> '\t' <empty>
问题在于连续的空条目。我收到的文件可以有从1到36个连续的管道符号(0到37个连续的空条目)。
澄清
解决方案不必是sed
,awk
,grep
,tr
等。这些只是我查看的解决方案。 perl
或python
脚本(或任何我没有想到的其他想法)也将受到欢迎。
我的尝试和研究
对于我在研究期间尝试的内容,包括命令及其输出作为图像1和文本文件2,以避免问题过于混乱。
我查找的链接 -- 使用sed
查找连续的管道符号(并替换任何这样的管道符号系列):参考此处;计算空字段的数量(可能有用,以了解需要多少个<empty>
):参考此处;最长序列:参考此处;
系统信息
$ uname -a
CYGWIN_NT-10.0 A-1052207 2.5.2(0.297/5/3) 2016-06-23 14:29 x86_64 Cygwin
$ bash --version
GNU bash, version 4.3.42(4)-release (x86_64-unknown-cygwin) ...
$
我正在Windows 10上运行此版本的Cygwin(因为工作需要)。
编辑1
我不太清楚具体需要什么。
以下是一个简短的示例,显示了我想要的管道开头和结尾:
(如果您输入第一行,按回车键,输入第二行,按回车键等,则会看到并需要键入此内容。无法复制/粘贴,因为>
只会在您按下前一行的回车键后出现。)
$ cat > myfile.txt<<EOF
> ||foo|||bar||
> EOF
$ <**command-to-be-used**> myfile.txt | cat -A
<empty>^I<empty>^Ifoo^I<empty>^I<empty>^Ibar^I<empty>^I<empty>$
^I
表示的是我的 bash
版本中的 '\t'
。从使用我提供的示例文本得到的答案中,我意识到我希望在 labore
后面加上一个 <empty>
(请参见下面的命令)。请注意,收到的答案(感谢 @Neil_McGuigan 和 @Ed_Morton)确实在 labore
之后给出了一个 '\t'
,只是没有 <empty>
。这是我的错误,因为我在最初的描述中没有表述清楚。我很抱歉。
通过对 @Neil_McGuigan 的命令进行微调,我成功地实现了我的目标。请注意,如果您想按照显示的方式 "逐行" 输入此命令,则需要在每行末尾包括一个空格和一个 \
。
$ echo "||lorem|ipsum||sit|amet,||||eiusmod|tempor|||labore|" |
awk '
{
$1=$1; n_empty=0;
for(i=1; i<=NF; i++)
{
if($i=="") {$i="<empty>"; n_empty++;}
};
print
}
END {print n_empty" entries are empty" | "cat 1>&2";}
' FS='|' OFS=$'\t'
| cat -A
给出结果:
<empty>^I<empty>^Ilorem^Iipsum^I<empty>^Isit^Iamet,^I<empty>^I<empty>^I<empty>^Ieiusmod^Itempor^I<empty>^I<empty>^Ilabore^I<empty>$
9 entries are empty
再次说明,对于那些不想滚动的人,此输出如下:
<empty>^I<empty>^Ilorem^Iipsum^I<empty>^Isit^Iamet,^I<empty>^I<empty>^I<empty>^Ieiusmod^Itempor^I<empty>^I<empty>^Ilabore^I<empty>$
有9个条目为空
(请注意,将空条目的计数写入stderr
并不是必要的,但很好。)
对于我所想要的内容没有表述清楚,非常抱歉。
我成功使用的方法
感谢@Neil_McGuigan和@Ed_Morton,我能够得到我所寻找的解决方案。我的最终命令如下:
$ awk '{$1=$1; n_empty=0; for(i=1; i<=NF; i++) {if($i=="") {$i="<empty>"; n_empty++;}}; print;} END {print n_empty" entries are empty" | "cat 1>&2";}' FS='|' OFS=$'\t' file_pipe-delim.txt > file_tab-delim.txt
$
如果您不想滚动,请查看相同的命令:
$ awk '{$1=$1; for(i=1; i<NF; i++){ if($(i)=="")$(i)="<empty>" }; print}'
FS='|' OFS=$'\t' file_pipe-delim.txt | sed 's/\t$/\t<empty>/g' >
file_tab-delim.txt
$
以下是一个制作、转换和保存文件的示例:
(如果您输入第一行,按Enter键,输入第二行,按Enter键等,则会看到并需要输入以下内容。它不能被复制/粘贴,因为>
符号只会在您在前一行按Enter键后出现。)
$ cat > file_pipe-delim.txt<<EOF
> ||dolor|sit
> amet,||adipiscing|
> sed|do|eiusmod|tempor
> |||
> |aliqua.|Ut|
> EOF
$ awk '{$1=$1; n_empty=0; for(i=1; i<=NF; i++)
{if($i=="") {$i="<empty>"; n_empty++;}}; print;} END
{print n_empty" entries are empty" | "cat 1>&2";}'
FS='|' OFS=$'\t' file_pipe-delim.txt > file_tab-delim.txt
$ cat -A file_tab-delim.txt
<empty>^I<empty>^Idolor^Isit$
amet,^I<empty>^Iadipiscing^I<empty>$
sed^Ido^Ieiusmod^Itempor$
<empty>^I<empty>^I<empty>^I<empty>$
<empty>^Ialiqua.^IUt^I<empty>$
$
最后,让我们返回那个让我头疼的字符串。我们可以按以下方式获得所需的输出:
$ echo "||lorem|ipsum||sit|amet,||||eiusmod|tempor|||labore|" | awk '{$1=$1; n_empty=0; for(i=1; i<=NF; i++) {if($i=="") {$i="<empty>"; n_empty++;}}; print;} END {print n_empty" entries are empty" | "cat 1>&2";}' FS='|' OFS=$'\t' | cat -A
<empty>^I<empty>^Ilorem^Iipsum^I<empty>^Isit^Iamet,^I<empty>^I<empty>^I<empty>^Ieiusmod^Itempor^I<empty>^I<empty>^Ilabore^I<empty>$
9 entries are empty
现在,我们执行同样的命令,但是不使用管道符号到 cat -A
,这意味着我们将不会看到每个 '\t'
的 ^I
;我们只会看到文本中的制表符。
$ echo "||lorem|ipsum||sit|amet,||||eiusmod|tempor|||labore|" | \
awk '{$1=$1; n_empty=0; for(i=1; i<=NF; i++) \
{if($i=="") {$i="<empty>"; n_empty++;}}; print;} END \
{print n_empty" entries are empty" | "cat 1>&2";}' \
FS='|' OFS=$'\t'
<empty> <empty> lorem ipsum <empty> sit amet, <empty> <empty> <empty>eiusmod tempor <empty> <empty> labore <empty>
9 entries are empty
a|||b
中的两个空字段的问题在于s/||/|<empty>|/g'
或类似的正则表达式会使用前两个管道符号进行第一次匹配,因此当扫描继续时,第三个管道符号没有成对。您可以通过重复原始匹配来克服这个问题:sed -e 's/||/|<empty>|/g' -e 's/||/|<empty>|/g'
。但是,当您也要更改分隔符时,就需要更加努力地工作,这就是问题所在。 - Jonathan Leffler